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;
    }
  • 相关阅读:
    转:CXF学习笔记一:如何创建、发布和访问基于CXF的服务
    转:Osgi实战中的问题
    转:用Spring JMS使异步消息变得简单
    jqueryeasyui分页组件的用法
    JavaScript高级程序设计(第三版)学习笔记一
    浅谈组建开发团队
    平常心
    狮子座与摩羯座 转载
    android项目实战(一)
    chrome安装media player无效的解决方法
  • 原文地址:https://www.cnblogs.com/Noevon/p/8406280.html
Copyright © 2011-2022 走看看