bzoj5406: Gift

全程膜拜 码得都要一样了。。

对于这种数列置换的可以理解成多个环,而对于一个大小为d的环把顺序弄对要做d-1次

总起来就是n-环数的次数 加上暴力30pt到手啦

假如题目没有限制,那就是第一类斯特林数,因为点有序再乘一个所有点的全排列 又20pt到手啦,考场上就溜了

现在考虑分情况讨论。如果我们让第一个数列的数字连向第二个数列的对应数字(或许这样理解环容易些),将有四种情况:上面是否有当前数字,下面是否有当前数字。给出的限制相当于提前先给了一些链

假如用二进制表示,0没有1有会方便点

对于11就不用管了,对于直接就是一个环的更不用管了,最后减一下直接是一个环的个数就好

主要问题在01和10,这两个是类似的,容易发现它们是不能互接成链的(废话要不就成11了),处理一个就行了吧。处理10的情况

我们先把出现10情况的数字个数处理出来

设f[i]表示这些数字构成的环数为i的方案数

当然这样的东西肯定是要上个容斥的,设g[i]是至少为i,要有g[i]=sigema(i~n)j f[j]*j的容斥系数

这样g[x]=sigema(x^k)i C(k,i)*S1(i,x)*(m+k-i)^(k-i)

k为10情况的数字个数,m为00情况的个数,含义为选i个数字,然后把它划分到x个环,剩下的k-i个10情况的数字可以选择接去其中一个状态为00的数字,连了边以后相当于一个10一个01,我们也可以看成一个11一个00,恰好抵消扔给00的情况自己处理,也可以接到一个未划分的或者不接,未划分的自己也会找其他的,不接就自环咯。因为选了就不能再选所以是降次幂

考虑f对g的贡献,对于fd,对gx的贡献为C(d,x),意为d中选x个环给选中,于是g[i]=sigema(i~n)j f[j]*C(j,i),直接二项式反演即可

00的情况就是20pt的做法

as[x]=∑∑∑f(i)g(j)h(k)(i+j+k==x) 分别是10,01,00的情况

做两次卷积,因为数据小那就不彂(fa)發(fa)闧(ta)了我不会NTT啊,直接暴力搞

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>

#define rcy(x) ((x)%2==0?1:-1)
using namespace std;
typedef long long LL;
const int _=100;
const int maxn=2*1e3+_;
const LL mod=998244353;

LL C[maxn][maxn],S[maxn][maxn],A[maxn][maxn];//i的降j次幂 
void yu()
{
    C[0][0]=S[0][0]=A[0][0]=1;
    for(int i=1;i<maxn;i++)
    {
        C[i][0]=A[i][0]=1;
        for(int j=1;j<=i;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod,
            S[i][j]=(S[i-1][j-1]+(i-1)*S[i-1][j])%mod,
            A[i][j]=A[i][j-1]*(i-j+1)%mod;
    }
}

//------------------------------------------def-------------------------------------------------------

bool vis[maxn*2];int n,nxt[maxn*2],cnt[4];//0-x,x-0,x-x,0-0
void dfs(int x,int st)
{
    vis[x]=true;
    if(nxt[x]==0)
    {
        if(x>n&&st>n)cnt[3]++;
        else if(x<=n&&st>n)cnt[0]++;
        else if(x>n&&st<=n)cnt[1]++;
    }
    else
    {
        if(!vis[nxt[x]])dfs(nxt[x],st);
        else cnt[2]++;
    }
}

//-------------------------------------------分类讨论---------------------------------------------- 

void calc(LL *u,int k)
{
    for(int x=0;x<=k;x++)
        for(int i=x;i<=k;i++)
            u[x]=(u[x]+C[k][i]*S[i][x]%mod*A[cnt[3]+k-i][k-i])%mod;
    
    for(int i=0;i<=k;i++)
    {
        for(int j=i+1;j<=k;j++)
            u[i]=(u[i]+rcy(j-i)*u[j]*C[j][i])%mod;
        u[i]=(u[i]+mod)%mod;
    }
}
LL f[maxn],g[maxn],h[maxn],as[maxn];
void solve()
{
    calc(f,cnt[0]),calc(g,cnt[1]);
    
    for(int i=0;i<=n;i++)
        for(int j=0;j<=i;j++)
            h[i]=(h[i]+f[j]*g[i-j])%mod;        
    for(int i=0;i<=n;i++)
        for(int j=0;j<=i;j++)
            as[i]=(as[i]+h[j]*S[cnt[3]][i-j])%mod;
            
    for(int i=0;i<=n;i++)as[i]=as[i]*A[cnt[3]][cnt[3]]%mod;
}

//--------------------------------------------solve-------------------------------------------------

int a[maxn],b[maxn]; bool ru[maxn*2];
int main()
{
    freopen("gift.in","r",stdin);
    freopen("gift.out","w",stdout);
    yu();
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    
    memset(vis,true,sizeof(vis));
    for(int i=1;i<=n;i++)
    {
        if(a[i]==0)a[i]=i+n;
        if(b[i]==0)b[i]=i+n;
        vis[a[i]]=vis[b[i]]=false;
        if(a[i]<=n||b[i]<=n)
            nxt[a[i]]=b[i],ru[b[i]]=true;
    }
    for(int i=1;i<=2*n;i++)
        if(!vis[i]&&!ru[i])dfs(i,i);
    for(int i=1;i<=2*n;i++)
        if(!vis[i])dfs(i,i);
        
    solve();
    for(int i=0;i<n;i++)
        printf("%lld ",n-cnt[2]-i>=0?as[n-cnt[2]-i]:0);
    puts("");
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/AKCqhzdy/p/10336882.html