zoukankan      html  css  js  c++  java
  • CF715E—— Complete the Permutations

    传送门:QAQQAQ

    题意:给你两个$1$~$n$的排列,0表示该位置数字不确定,两两交换第一个排列中的元素使之变成第二个排列,令$s[x]$表示对于所有不同的两个排列,最少交换次数为$x$的序列有$s[x]$个,求$x=0$~$n-1$所有的$s[i]$

    思路:简直神题QAQ,不愧CF3400,思路十分巧妙

    直接贴上大佬的题解:

      这里对于题解加上一些个人的理解:因为最少交换数就是$n-Cycle(G)$,所以我们可以统计不同连边情况对于环个数的贡献

      如果是数字单独成环,那么这个环仅有一个且不会变化,最后算进即可,如果数字成链,既不可能分成多个环,也不可能单独成环,最终肯定接在0-0两端,且只有一种情况,所以对答案不会有贡献

      那么剩下的就是统计$0-x$,$x-0$,$0-0$的组成不同个数环的方案数,用到第一类斯特林数(表示$i$个数摆成$j$个圆排列的方案个数,因为确定一个圆排列,0的值就可以确定了,即对应一种方案(除了$0-0$,两个接口都是0还要乘阶乘)),生成函数见上(用i表示i个环的方案数)

      ($x-0$接$0-x$必须要$0-0$过度,且$x-0$,$0-x$接进$0-0$对环的个数没有任何影响,因为开头结尾依然是$0-0$,且都是一条链,这些没有埋进去的才进行斯特林数统计)

      有一点要注意,就是$0-0$要乘$va!$,因为有$va$个$0-0$,圆排列即有$va$个接口,每个接口的数值都是随便的,所以要乘上阶乘;而一段有值接上时另一端点0就被赋值成了那个值,只有1种情况

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int MOD=998244353;
    const int N=5005;
    
    int ADD(int &x,int y)
    {
        x+=y;
        if(x>=MOD) x%=MOD;
    }
    
    int DEC(int &x,int y)
    {
        x=x+MOD-y;
        if(x>=MOD) x-=MOD; 
    }
    
    int n,p[N],q[N];
    int dp[N][N],C[N][N],base[N];
    void init()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&p[i]);
        for(int i=1;i<=n;i++) scanf("%d",&q[i]);
        dp[0][0]=1;//(first stirling)
        for(int i=1;i<=n;i++)
            for(int j=0;j<=n;j++) dp[i][j]=(1LL*dp[i-1][j]*(i-1)+dp[i-1][j-1])%MOD;
        C[0][0]=1;
        for(int i=1;i<=n;i++)
        {
            C[i][0]=1;
            for(int j=1;j<=n;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
        }
        base[0]=1;
        for(int i=1;i<=n;i++) base[i]=1LL*base[i-1]*i%MOD;
    }
    
    void run(int *a,int *b,int *c)
    {
        int ret[N*2];
        memset(ret,0,sizeof(ret));//!!!
        for(int i=0;i<=n;i++)
            for(int j=0;j<=n;j++) ret[i+j]=(ret[i+j]+1LL*a[i]*b[j]%MOD)%MOD;
        for(int i=0;i<=n;i++) c[i]=ret[i];
    }
    
    //p->q
    int va=0,vb=0,vc=0,vd=0;//0-0  0-x  x-0  x-x
    int bln[N],blp[N];//nxt/pre
    int pre[N],nxt[N],vis[N];
    int F[N*3],G[N*3],H[N*3];
    void ready()
    {
        for(int i=1;i<=n;i++)
        {
            if(p[i]&&q[i]) nxt[p[i]]=q[i],pre[q[i]]=p[i];
            else if(p[i]) bln[p[i]]=1; 
            else if(q[i]) blp[q[i]]=1;
            else va++;
        }
        for(int i=1;i<=n;i++)//value 
        {
            if(vis[i]) continue;
            vis[i]=1;
            int l=i,r=i;
            while(!vis[pre[l]]&&pre[l]) l=pre[l],vis[l]=1;
            while(!vis[nxt[r]]&&nxt[r]) r=nxt[r],vis[r]=1;
            if(nxt[r]==l) vd++;
            else if(blp[l]&&!bln[r]) vb++;
            else if(!blp[l]&&bln[r]) vc++;
            else if(blp[l]&&bln[r]) va++; 
        }
        //cout<<va<<" "<<vb<<" "<<vc<<" "<<vd<<endl;
    }
    
    int ans[N];
    void solve()
    {
        int tmp;
        for(int i=0;i<=vb;i++)
            for(int j=i;j<=vb;j++) 
            {
                if(j==vb) tmp=1;
                else tmp=C[va+vb-j-1][va-1];//¿ÉÄÜva=0 µ½-1 
                ADD(G[i],1LL*dp[j][i]*C[vb][j]%MOD*tmp%MOD*base[vb-j]%MOD);
            }
        for(int i=0;i<=vc;i++)
            for(int j=i;j<=vc;j++) 
            {
                if(j==vc) tmp=1;
                else tmp=C[va+vc-j-1][va-1];
                ADD(H[i],1LL*dp[j][i]*C[vc][j]%MOD*tmp%MOD*base[vc-j]%MOD);
            }
        for(int i=0;i<=va;i++) ADD(F[i],1LL*dp[va][i]*base[va]%MOD);
        run(F,G,F);
        run(F,H,F);
        for(int i=0;i<=va+vb+vc;i++) ans[n-i-vd]=F[i];
        for(int i=0;i<n;i++) printf("%d ",ans[i]);
    }
    
    int main()
    {
        init();
        ready();
        solve();
        return 0;
    }
  • 相关阅读:
    重定向请求
    json处理
    post请求
    get请求
    提交cookie登录
    进击的Python【第三章】:Python基础(三)
    进击的Python【第二章】:Python基础(二)
    进击的Python【第一章】:Python背景初探与Python基础(一)
    java 内存分析
    java--循环练习
  • 原文地址:https://www.cnblogs.com/Forever-666/p/13155463.html
Copyright © 2011-2022 走看看