BZOJ4770: 图样

138 篇文章 0 订阅
12 篇文章 0 订阅

我们令 f[n][k] f [ n ] [ k ] 表示n个点,每个点点权在 [0,2k+1) [ 0 , 2 k + 1 ) ,MST边权和的期望
转移的时候,我们枚举有i个点第k位为1,n-i个点第k位为0,则最后的MST一定是i个点和n-i个点之间连一条边,剩下i个点,n-i个点个组成一棵生成树,而这个可以被划分成子问题
现在的问题在于i个点和n-i个点之间连一条边,这条边一定是i(n-i)条边里面最小的,那么这条边边权的期望怎么算
我们令 g[n][m][k] g [ n ] [ m ] [ k ] 表示n个点和m个点之间连一条边,每个点点权在 [0,2k+1) [ 0 , 2 k + 1 ) 间,nm条边里最小的那条边边权的期望,那么上面的期望就是 2k+g[i][ni][k1] 2 k + g [ i ] [ n − i ] [ k − 1 ]

g仍然不能直接dp,还需要一个辅助的状态
我们令 h[n][m][k][l] h [ n ] [ m ] [ k ] [ l ] 表示每个点点权在 [0,2k+1) [ 0 , 2 k + 1 ) 间,n个点和m个点之间连的所有边边权都 >=l >= l 的概率
转移的时候仍然枚举各自有多少个点在第k位为1,有

h[n][m][k][l]=x,yCxnCym2n+mh[x][y][k1][l]h[nx][my][k1][l]h[x][my][k1][l2k]h[nx][y][k1][l2k] h [ n ] [ m ] [ k ] [ l ] = ∑ x , y C n x C m y 2 n + m h [ x ] [ y ] [ k − 1 ] [ l ] ∗ h [ n − x ] [ m − y ] [ k − 1 ] [ l ] ∗ h [ x ] [ m − y ] [ k − 1 ] [ l − 2 k ] ∗ h [ n − x ] [ y ] [ k − 1 ] [ l − 2 k ]
(边界要注意一下)

dp出h后,有

g[n][m][k]=l>0h[n][m][k][l] g [ n ] [ m ] [ k ] = ∑ l > 0 h [ n ] [ m ] [ k ] [ l ]

然后有

f[n][k]=iCin2n(f[i][k1]+f[ni][k1]+(g[i][ni][k1]+2k)[i(ni)>0]) f [ n ] [ k ] = ∑ i C n i 2 n ( f [ i ] [ k − 1 ] + f [ n − i ] [ k − 1 ] + ( g [ i ] [ n − i ] [ k − 1 ] + 2 k ) [ i ( n − i ) > 0 ] )

复杂度 O(n4m2m) O ( n 4 m 2 m ) ,完全不知道怎么过,但是因为 nm n m 不大,可以把所有 f[n][m] f [ n ] [ m ] 打个表预处理出来

code(用来打表的程序):

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 55;
const int maxk = 8;
const int mod  = 258280327;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;}

int pw(int x,int k)
{
    int re=1;
    for(;k;k>>=1,x=(ll)x*x%mod) if(k&1)
        re=(ll)re*x%mod;
    return re;
}
int inv(int x){ return pw(x,mod-2); }

int n,m;
int C[maxn][maxn],inv2[maxn];
int f[maxn][maxk],g[maxn][maxn][maxk],h[maxn][maxn][maxk][1<<maxk];

void dph()
{
    for(int i=1;i<=n;i++) for(int j=1;i+j<=n;j++)
        h[i][j][0][1]=inv2[i+j-1];
    for(int k=1;k<m;k++)
    {
        for(int i=1;i<=n;i++) for(int j=1;i+j<=n;j++)
        {
            for(int l=1;l<(1<<k+1);l++)
            {
                for(int x=0;x<=i;x++) for(int y=0;y<=j;y++)
                {
                    int p=(ll)C[i][x]*C[j][y]%mod;
                    if(x&&y) p=(ll)p*h[x][y][k-1][l]%mod;
                    if(i-x&&j-y) p=(ll)p*h[i-x][j-y][k-1][l]%mod;
                    if(x&&j-y&&(1<<k)<l) p=(ll)p*h[x][j-y][k-1][l-(1<<k)]%mod;
                    if(i-x&&y&&(1<<k)<l) p=(ll)p*h[i-x][y][k-1][l-(1<<k)]%mod;
                    add(h[i][j][k][l],p);
                }
                h[i][j][k][l]=(ll)h[i][j][k][l]*inv2[i+j]%mod;
            }
        }
    }
}
void dpg()
{
    for(int i=1;i<=n;i++) for(int j=1;i+j<=n;j++) for(int k=0;k<m;k++)
        for(int l=1;l<(1<<k+1);l++) add(g[i][j][k],h[i][j][k][l]);
}
void dpf()
{
    for(int i=1;i<=n;i++) 
    {
        for(int j=0;j<=i;j++)
        {
            int p=C[i][j],c=0;
            if(j&&i-j) c=1;
            add(f[i][0],p*c);
        }
        f[i][0]=(ll)f[i][0]*inv2[i]%mod;
    }
    for(int k=1;k<m;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=i;j++)
            {
                int p=C[i][j],c=0;
                if(j&&i-j) c=g[j][i-j][k-1]+(1<<k);
                if(j) add(c,f[j][k-1]);
                if(i-j) add(c,f[i-j][k-1]);
                add(f[i][k],(ll)p*c%mod);
            }
            f[i][k]=(ll)f[i][k]*inv2[i]%mod;
        }
    }
}

int main()
{
    freopen("tmp.in","r",stdin);
    freopen("tmp.out","w",stdout);

    //scanf("%d%d",&n,&m);
    n=50; m=8;

    C[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
    inv2[0]=1,inv2[1]=(mod+1)>>1;
    for(int i=2;i<=n;i++) inv2[i]=(ll)inv2[i-1]*inv2[1]%mod;

    dph();
    dpg();
    dpf();

    for(int i=1;i<=n;i++) for(int j=0;j<m;j++) printf("%d,",f[i][j]);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值