• 2018牛客网暑期ACM多校训练营(第一场)B Symmetric Matrix(思维+数列递推)


    题意

    给出一个矩阵,矩阵每行的和必须为2,且是一个主对称矩阵。问你大小为n的这样的合法矩阵有多少个。

    分析

    作者:美食不可负064
    链接:https://www.nowcoder.com/discuss/87226?type=101&order=0&pos=1&page=1
    来源:牛客网

    题目给出的合法矩阵是一个类似与邻接矩阵的样式。 所以应该往这方面去考虑。
    每行之和等于2 , 代表每个点都连有两条边,可以有重边 不能有自环。
    这说明 每个点属于且仅属于一个环。
    因为输入只有一个n
    应该要往dp递推的方向上去想。

    现在开始找递推式。
    定义dp[n]表示n个点构成的合法图的方案数。
    思考每加入一个新球,如何从已知状态转移。
    考虑从前面的n-1个球中选取一些球和新球组成一个环。
    特殊考虑只取一个旧球的情况,
    这种情况下这个旧球有n-1种方案 剩下的n-2个球组成的合法方案数已经求出。
    所以这种情况下 方案数为(n-1)f(n-2)
    推广到一般情况
    当我们取k个旧球,剩下的球与新球组成环时,旧球的取法有C(n-1,k) ,剩下的旧球与新球组成环的方案数有(n-1-k)!种
    但是考虑对称性 需要除以2。 又考虑到只取一个球的时候不需要考虑对称性 ,所以把这种情况单独摘出来考虑。
    最后得到 dp[n]的递推式就是
    dp[n] = (n-1) dp[n-2] + sigma(x:2->n-3)((n-1)!/(2*x!)dp[x])
    但是这个东西有一个讨厌的sigma 我们可以通过相减的方法来消除这个sigma。

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<vector>
    #include<cstdio>
    #include<algorithm>
    #include<map>
    #include<set>
    #define rep(i,e) for(int i=0;i<(e);i++)
    #define rep1(i,e) for(int i=1;i<=(e);i++)
    #define repx(i,x,e) for(int i=(x);i<=(e);i++)
    #define X first
    #define Y second
    #define PB push_back
    #define MP make_pair
    #define mset(var,val) memset(var,val,sizeof(var))
    #define scd(a) scanf("%d",&a)
    #define scdd(a,b) scanf("%d%d",&a,&b)
    #define scddd(a,b,c) scanf("%d%d%d",&a,&b,&c)
    #define pd(a) printf("%d
    ",a)
    #define scl(a) scanf("%lld",&a)
    #define scll(a,b) scanf("%lld%lld",&a,&b)
    #define sclll(a,b,c) scanf("%lld%lld%lld",&a,&b,&c)
    #define IOS ios::sync_with_stdio(false);cin.tie(0)
    #define lc idx<<1
    #define rc idx<<1|1
    #define rson mid+1,r,rc
    #define lson l,mid,lc
    using namespace std;
    typedef long long ll;
    template <class T>
    void test(T a){cout<<a<<endl;}
    template <class T,class T2>
    void test(T a,T2 b){cout<<a<<" "<<b<<endl;}
    template <class T,class T2,class T3>
    void test(T a,T2 b,T3 c){cout<<a<<" "<<b<<" "<<c<<endl;}
    const int inf = 0x3f3f3f3f;
    const ll INF = 0x3f3f3f3f3f3f3f3fll;
    const ll mod = 1e9+7;
    int T;
    void testcase(){
        printf("Case %d: ",++T);
    }
    const int MAXN = 1e6+10;
    const int MAXM = 30;
    
    ll dp[MAXN];
    
    int main() {
    #ifdef LOCAL
        freopen("in.txt","r",stdin);
    #endif // LOCAL
        ll n,m;
        dp[1]=0;
        dp[2]=dp[3]=1;
        while(cin>>n>>m){
            for(ll i=4;i<=n;i++){
                dp[i]=((i-1)*dp[i-1]%m + (i-1)*dp[i-2]%m+m-(i-1)*(i-2)/2%m*dp[i-3]%m)%m;
            }
            cout<<dp[n]<<endl;
        }
        return 0;
    }
  • 相关阅读:
    JavaScript打造很酷的图片放大效果实例代码
    【荐】CSS实现的鼠标点击小图无刷新放大图片代码
    JavaScript+CSS实现的文字幻灯切换代码
    【荐】很棒的图片友情链接带控制按钮的横向滚动代码
    jquery制作一个漂亮带渐隐效果的跑动区域
    JS打造的一款响应鼠标变化很炫的图片特效代码
    JS+CSS控制鼠标移上图片滑出文字提示代码
    Jquery+CSS实现的大气漂亮图片切换效果代码
    【荐】JS+CSS防FLASH效果带倒影的图片切换效果代码
    JavaScript限制对图片右键代码
  • 原文地址:https://www.cnblogs.com/fht-litost/p/9351271.html
Copyright © 2020-2023  润新知