zoukankan      html  css  js  c++  java
  • 51nod1835 完全图

    题意:输入n,m(n,m<500),问有n的点且联通块为m个的图有多少

    题解:

    dp[i][j]代表取前i个形成j个联通块的个数
    第i个单独考虑,可以枚举联通块的大小K:[1, i-j+1]
    要保证枚举的时候方案不会重复,那么不能写作c(i, k)
    这样dp[i1][j]和dp[i2][j]会有重复的部分
    所以这里固定下来第i个是必须要取的
    那么方案数就是c(i-1, k-1)
    所以可以得到递推式
    for(int k=1;k<=i-j+1;k++)
    dp[i][j] += c(i-1, k-1)*dp[i-k][j-1]*dp[k][1];
    dp[k][1]要单独计算,它的意义是k个数形成一个联通块的方案数:
    dp[k][1] = 2^(k*(k-1)/2);
    for(int i=2;i<=k;i++) dp[k][1] -= dp[k][i];
    注意题目要求至少一条边,所以当m==1时,答案要减1

    要预处理

    #include <bits/stdc++.h>
    #define ll long long
    #define maxn 510
    using namespace std;
    const ll mod = 998244353;
    ll c[maxn][maxn], n, m, dp[maxn][maxn];
    void init(){
        for(ll i=0;i<=500;i++){
            c[i][0] = c[i][i] = 1;
            for(ll j=1;j<i;j++)
                c[i][j] = (c[i-1][j-1]+c[i-1][j])%mod;
        }
    }
    ll f(ll a,ll b){
        ll ans = 1;
        a = a%mod;
        while(b){
            if(b&1) ans = ans*a%mod;
            a = a*a%mod;
            b >>= 1;
        }
        return ans;
    }
    int main(){
        init();
        scanf("%lld%lld", &n, &m);
        for(ll i=1;i<=n;i++){
            for(ll j=2;j<=i;j++)
                for(ll k=1;k<=i-j+1;k++)
                    dp[i][j] = (dp[i][j]+c[i-1][k-1]*dp[i-k][j-1]%mod*dp[k][1]%mod)%mod;
            dp[i][1] = f(2, (i*(i-1)/2));
            for(ll j=2;j<=i;j++)
                dp[i][1] = (dp[i][1]+mod-dp[i][j])%mod;
        }
        if(m == 1) printf("%lld
    ", dp[n][1]-1);
        else printf("%lld
    ", dp[n][m]);
        return 0;
    }
  • 相关阅读:
    JQuery中的AJAX
    (十六)JQuery Ready和angularJS controller的运行顺序问题
    Excel自己定义纸张打印设置碰到无法对上尺寸的问题
    MVC整个样例的源代码
    将Image转化为BufferImage
    Graphics samples2
    Graphics samples
    please tell me the error about java Graphics
    获取网络资源大小
    java通过地址获取主机名
  • 原文地址:https://www.cnblogs.com/Noevon/p/8406280.html
Copyright © 2011-2022 走看看