51nod1835 完全图 容斥+dp

28 篇文章 0 订阅
15 篇文章 0 订阅

Description


初始有n个点,任意两个点之间有一条无向边,现在要移除一些无向边(至少一条),问移除后有恰好m个连通块的方案数是多少。
两个方案不同当且仅当存在至少一条无向边在某个方案中被移除,但是在另一个方案中没被移除。
答案可能很大请模一个998,244,353。

Input


第一行读入n,m。
1<=m<=n<=500

Output


第一行输出方案数。

Solution


我好弱啊,算法马拉松只会这题,还T了

首先讲一下怎么做m=1的情况

考虑容斥。设 f[i][1] 表示任意i个点形成了1个连通块的方案数,那么 f[i][1]=2i(i1)2Ci1j1(ij)(ij1)2f[j][1] 其中j是我们枚举的1所在连通块的size,每次用总的方案减去不合法的方案,即可以任意从i-1个剩下点中拉j-1个点与1相连,然后剩下的随便连

类似的, f[i][j]=Ci1k1f[ik][j1]f[k][1] k表示枚举的1所在连通块的size

这里的组合数最好打杨辉三角形的表不然容易T
考虑到题目中提到的至少删一条边,我们要在m=1时去掉完全图的方案

Code


#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define ll long long
#define N 505
#define M 224751
#define MOD 998244353
ll f[N][N],c[N][N],jc[M];
int n,m;
void init() {
    rep(i,0,n) {
        c[i][0]=1;
        rep(j,1,i) {
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
        }
    }
    int lim=c[n][2];
    jc[0]=1;
    rep(i,1,lim) jc[i]=(jc[i-1]+jc[i-1])%MOD;
}
ll ksm(ll x,ll dep) {
    ll ret=1;
    for (;dep;dep>>=1,x=x*x%MOD) {
        if (dep&1) {
            ret=ret*x%MOD;
        }
    }
    return ret;
}
int main(void) {
    scanf("%d%d",&n,&m);
    init();
    f[1][1]=1;
    ll tmp=0;
    rep(i,2,n) {
        f[i][1]=jc[c[i][2]];
        rep(j,1,i-1) {
            tmp=c[i-1][j-1];
            f[i][1]=(f[i][1]+MOD-c[i-1][j-1]*jc[c[i-j][2]]%MOD*f[j][1]%MOD)%MOD;
        }
    }
    rep(j,2,m) {
        rep(i,j,n) {
            rep(k,1,i-1) {
                f[i][j]=(f[i][j]+c[i-1][k-1]*f[i-k][j-1]%MOD*f[k][1]%MOD)%MOD;
            }
        }
    }
    if (m==1) {
        f[n][m]+=MOD-1;
        f[n][m]%=MOD;
    }
    printf("%lld\n",f[n][m]);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值