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;
    }
  • 相关阅读:
    JS中关于clientWidth offsetWidth scrollWidth 等的含义
    javascript中数组concat()join()split()
    我的大数据学习路线(持续更新)
    java多线程-学习笔记
    java多线程-线程交互&互斥&同步
    java多线程-关键人物程咬金
    java多线程-军队战争
    java多线程-两个演员线程
    pytorch-Flatten操作
    龙良曲pytorch学习笔记_迁移学习
  • 原文地址:https://www.cnblogs.com/Noevon/p/8406280.html
Copyright © 2011-2022 走看看