20200104模拟赛 问题A 图样

题目

分析:

老规矩,遇到期望要准备好随时投降。。。

大致想到了按位处理,然后分别下去搜索,再用组合数加加减减一下。。。

但是两个连通块之间连边的期望怎么算呢?

很好,投降。。。

下来看题解。。。

果然是记搜。。

首先我们设F(n,m)表示n个点取 [ 0 , 2^m )的值时所有最小生成树代价之和

那么Ans=F(n,m) / 2^(n*m)

再设G(S,T,m)表示一部分点集大小为S,另一部分大小为T,点权取值在[ 0 , 2^m )之间后,所有情况最小边权值的总和

于是F(n,m)可以记搜:

F(n,m)=

sigma(i=1...n) C(n,i)(选哪些点) * (

F( i , m-1 ) * 2^( (n-i) * (m-1) )(一部分向下搜索再乘方案数) +

F( n-i , m-1 ) * 2^( i * (m-1) )(另一部分) +

G( i , n-i , m-1)(中间连边) +

2 ^ (m-1) * 2 ^ ( n * (m-1) )(必须花费的代价乘上方案数) )

然后我们来求G:

我们叒设一个函数P(S,T,m,K)表示点集S,T,取值[ 0 , 2^m )时,边权最小值大于等于K的情况数

G(S,T,m)可以巧妙地化为sigma(i=1...(2^m-1))P(S,T,m,i)

奥妙重重,可以脑补一下,跟前缀和差不多的感觉

然后P就很好求了,暴力地将S和T继续按01位分割下去,某一位全部都不分割时统计入答案

三重记搜,式子还这么难

神仙题Orz

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>

#define maxn 55
#define maxm 9
#define MOD 258280327

using namespace std;

inline long long getint()
{
    long long num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
    return num*flag;
}

int n,m;
long long C[maxn][maxn],F[maxn][maxm],G[maxn][maxn][maxm],P[maxn][maxn][maxm][1<<maxm],pw[maxn*maxm];

inline long long ksm(long long num,long long k)
{
    long long ret=1;
    for(;k;k>>=1,num=num*num%MOD)if(k&1)ret=ret*num%MOD;
    return ret;
}

inline long long getP(int S,int T,int M,int K)
{
    if(S>T)swap(S,T);
    if(!S||K<=0)return pw[(S+T)*M];
    if(K>=(1<<M))return 0;
    if(~P[S][T][M][K])return P[S][T][M][K];
    long long tmp=0;
    for(int i=0;i<=S;i++)for(int j=0;j<=T;j++)
        if((i==0&&j==T)||(i==S&&j==0))tmp=(tmp+getP(S,T,M-1,K-(1<<(M-1))))%MOD;
        else tmp=(tmp+getP(i,j,M-1,K)*getP(S-i,T-j,M-1,K)%MOD*C[S][i]%MOD*C[T][j]%MOD)%MOD;
    return P[S][T][M][K]=tmp;
}

inline long long getG(int S,int T,int M)
{
    if(!M)return 0;
    if(S>T)swap(S,T);
    if(~G[S][T][M])return G[S][T][M];
    long long tmp=0;
    for(int i=1;i<(1<<M);i++)
        tmp=(tmp+getP(S,T,M,i))%MOD;
    return G[S][T][M]=tmp;
}

inline long long getF(int N,int M)
{
    if(!M||N<2)return 0;
    if(~F[N][M])return F[N][M];
    long long tmp=2*getF(N,M-1)%MOD;
    for(int i=1;i<N;i++)
        tmp=(tmp+C[N][i]*(getF(i,M-1)*pw[(N-i)*(M-1)]%MOD+getF(N-i,M-1)*pw[i*(M-1)]%MOD+getG(i,N-i,M-1)+(1<<(M-1))*pw[N*(M-1)]%MOD))%MOD;
    return F[N][M]=tmp;
}

int main()
{
    n=getint(),m=getint();
    pw[0]=1;for(int i=1;i<=n*m;i++)pw[i]=pw[i-1]*2%MOD;
    for(int i=0;i<=n;i++)
    {
        C[i][0]=C[i][i]=1;
        for(int j=1;j<i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
    }
    memset(F,-1,sizeof F),memset(G,-1,sizeof G),memset(P,-1,sizeof P);
    printf("%lld\n",getF(n,m)*ksm(pw[n*m],MOD-2)%MOD);
}
View Code

猜你喜欢

转载自www.cnblogs.com/Darknesses/p/12149853.html