• CF715E. Complete the Permutations


    给出两个排列(p,q),其中有些位置不确定。

    定义(p,q)的距离为(p)进行最少多少次交换操作之后可以得到(q)

    问所有的合法方案下距离为(k)(k=0dots n-1))的方案数。

    (nle 250)(实际上可以做(nle 5000)


    官方题解不知道在搞什么,时间复杂度是(O(n^3lg n))(用NTT)或(O(n^{3.58}))(用叫Karatsuba的快速乘法)。

    下面是(O(n^2))的阳间做法:

    假如(p,q)已经确定,(p)(q)的距离:建图,连边(p_i,q_i),距离为(n-轮换数)

    现在分析一下图中四种边(由于只关心头尾,所以链可以视作边):((0,x),(x,0),(0,0),(x,y))。如果出现((x,y))就把(x,y)缩点并把这条边去掉,如果存在((x_1,0))((0,x_2))(x_1=x_2),把这两条边去掉并换成((0,0))。这样处理过后同一个(x)只会出现在一条边中。记最终剩下的三种边分别为一二三类边,个数分别为(a,b,c)。(稍微注意还要处理一下本来就存在的环)

    考虑这些边连上之后环的组成。发现:

    1. 都是一类边,或都是二类边。(下文分别称其为一类环、二类环)
    2. 一类边和二类边都有,其中一类边到二类边中间有三类边连接。

    分别先只考虑一类边和二类边。设(F_i)为此时恰好有(i)个一类环的方案数,设(f_i)为至少,算出(f)反演可得(F)。则有:

    [f_x=sum_{j=x}^a inom{a}{j}s(j,x)(a-j+c)^{underline{a-j}} ]

    其中(s)表示斯特林数。

    意义:枚举(j)条一类边组成(x)个环,剩下的(a-j)条边都需要接后继,((0,x))后面可以接((0,x))((0,0))

    同理可以求出(G_i)表示恰好有(i)个二类环的方案数。

    接下来考虑杂环的组成成分:可以发现,在计算一类边的拼接操作时,如果有((0,x))后面接((0,0)),可以视作两者合并成((0,0));二类边同理。所以可以视作:拼完了一类环和二类环,剩下的杂环全部由三类边组成,三类边还是(c)个(其实在前面,可以视作先拼一类边再拼二类边,由于拼二类边的时候三类边个数还是(c),所以并没有发生矛盾)。

    (H_i=s(c,i))。于是((F*G*H)_i)为恰好有(i)个环的方案数的生成函数。

    时间(O(n^2))


    using namespace std;
    #include <bits/stdc++.h>
    #define N 2505
    #define ll long long
    #define mo 998244353
    ll qpow(ll x,ll y=mo-2){
    	ll r=1;
    	for (;y;y>>=1,x=x*x%mo)
    		if (y&1)
    			r=r*x%mo;
    	return r;
    }
    int n;
    int p[N],q[N];
    ll fac[N],ifac[N];
    ll s[N][N];
    void initCs(){
    	fac[0]=1;
    	for (int i=1;i<=n;++i)
    		fac[i]=fac[i-1]*i%mo;
    	ifac[n]=qpow(fac[n]);
    	for (int i=n-1;i>=0;--i)
    		ifac[i]=ifac[i+1]*(i+1)%mo;
    	s[0][0]=1;
    	for (int i=1;i<=n;++i)
    		for (int j=1;j<=i;++j)
    			s[i][j]=(s[i-1][j-1]+(i-1)*s[i-1][j])%mo;
    }
    ll C(int m,int n){return fac[m]*ifac[n]%mo*ifac[m-n]%mo;}
    int a,b,c,o;
    int dsu[N];
    int getdsu(int x){return dsu[x]==x?x:dsu[x]=getdsu(dsu[x]);}
    void initabc(){
    	for (int i=1;i<=n;++i)
    		dsu[i]=i;
    	for (int i=1;i<=n;++i){
    		int u=getdsu(p[i]),v=getdsu(q[i]);
    		if (u && v)
    			o+=(dsu[u]==dsu[v]),dsu[u]=v;
    	}
    	static int bz[N];
    	for (int i=1;i<=n;++i){
    		int u=getdsu(p[i]),v=getdsu(q[i]);
    		if (!u && !v)
    			c++;
    		else if (!u && v){
    			if (bz[v]==1)
    				c++,b--;
    			else				
    				bz[v]=-1,a++;
    		}
    		else if (u && !v){
    			if (bz[u]==-1)
    				c++,a--;
    			else
    				bz[u]=1,b++;
    		}
    	}
    }
    int f[N],g[N],h[N],pro[N];
    void rev(int a,int f[]){
    	static int F[N];
    	for (int i=0;i<=a;++i){
    		ll sum=0;
    		for (int j=i;j<=a;++j)
    			(sum+=f[j]*C(j,i)*(j-i&1?-1:1))%=mo;
    		F[i]=(sum+mo)%mo;
    	}
    	memcpy(f,F,sizeof(int)*(a+1));
    }
    void calcfg(int a,int f[]){
    	for (int i=0;i<=a;++i){
    		ll sum=0;
    		for (int j=i;j<=a;++j)
    			(sum+=C(a,j)*s[j][i]%mo*fac[a-j+c]%mo*ifac[c])%=mo;
    		f[i]=sum;
    	}
    	rev(a,f);
    }
    void multi(int c[],int a[],int b[]){
    	static int t[N];
    	for (int i=0;i<=n;++i){
    		ll sum=0;
    		for (int j=0;j<=i;++j)
    			sum=(sum+(ll)a[j]*b[i-j])%mo;
    		t[i]=sum;
    	}
    	memcpy(c,t,sizeof(int)*(n+1));
    }
    int main(){
    //	freopen("in.txt","r",stdin);
    	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]);
    	initCs();
    	initabc();
    	calcfg(a,f);
    	calcfg(b,g);
    	for (int i=0;i<=c;++i)
    		h[i]=s[c][i];
    	multi(pro,f,g);
    	multi(pro,pro,h);
    	for (int i=0;i<n;++i)
    		printf("%d ",(n-o-i>=0?pro[n-o-i]:0)*fac[c]%mo);
    	return 0;
    }
    
  • 相关阅读:
    java 下载图片并传输(java自带 BASE64工具进行图片和字符串转换)
    MySQL的日期格式
    eclipse下查看maven下载的源码中文乱码问题
    Linux----部署
    python----logging
    python----pymysql
    vmware15 激活秘钥
    vmware15 激活秘钥
    Ubuntu18.04安装
    msyql45讲 20--幻读是什么,幻读有什么问题?
  • 原文地址:https://www.cnblogs.com/jz-597/p/14425866.html
Copyright © 2020-2023  润新知