ZOJ3874 Permutation Graph
原题地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5482
题意:
多组数据。
对于一个排列{a1,a2,… an},如果他把每一对(ai,aj)满足 i< j 且 ai>aj(即逆序对)连接起来,会得到一张图。
例如,如果排列是{2,3,1,4},则1和2连接,1和3连接。
给出得到的图的联通块个数、大小和每个联通块包含哪些数,问可以得到这些联通块的排列有多少种。
数据范围
n <= 130000
题解:
可以推得:
一个联通块只能是一段连续的数字。
块与块之间不存在逆序对的方案是唯一的,就是从小到达排列。
于是转化成每个联通块的方案数之积。
由于方案数只和长度有关,因此可以考虑DP。
最开始陷入了一个思维误区,总是想从dp[i-1]推dp[i],这样考虑加入一个最大/最小的数,反而没什么思路。
实际上枚举的i个点不联通的情况就是之前推导的块间不联通的情况,同样一个联通块内的还是只能连续,且块与块从小到大排列(i< j 则 max(i)< min(j))。
那么考虑用总共的减去不联通的,枚举第一块(1所属的块)大小就行了:
(前面是
1...j
1...
j
的联通方案,后面数比j大,肯定是没有边的,所以直接就
(i−j)!
(
i
−
j
)
!
)
fi=i!−∑j=1i−1fj∗(i−j)! f i = i ! − ∑ j = 1 i − 1 f j ∗ ( i − j ) !
后面是一个卷积的形式,于是采用分治NTT求解即可。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int mod=786433; //3*2^18+1 g=10
const int N=100500;
const int MXN=262144+100;
const int inf=0x3f3f3f3f;
int T,n,m,fac[N],a[MXN],b[MXN],w[MXN],_w[MXN],R[MXN],iv[MXN],G=10,f[N];
int modpow(int A,int B)
{
int ret=1; int base=A;
for(;B;B>>=1)
{
if(B&1) ret=(1LL*ret*base)%mod;
base=(1LL*base*base)%mod;
}
return ret;
}
void NTT(int *x,int opt,int len)
{
for(int i=0;i<len;i++) if(i<R[i]) swap(x[i],x[R[i]]);
for(int m=2;m<=len;m<<=1)
{
int l=m/2; int wn=(opt==1)?w[m]:_w[m];
for(int j=0;j<len;j+=m)
for(int i=0,wi=1;i<l;i++)
{
int y=(1LL*wi*x[i+j+l])%mod;
x[i+j+l]=(x[i+j]-y+mod)%mod;
x[i+j]=(x[i+j]+y)%mod;
wi=(1LL*wi*wn)%mod;
}
}
if(opt==-1) for(int i=0;i<len;i++) x[i]=(1LL*x[i]*iv[len])%mod;
}
void solve(int lf,int rg)
{
if(lf==rg){f[lf]=(fac[lf]-f[lf]+mod)%mod; return;}
int mid=(lf+rg)>>1;
solve(lf,mid);
int len=1,p=0;
for(;len<2*(rg-lf+1);len<<=1,p++);
for(int i=0;i<=len;i++) a[i]=b[i]=0;
R[0]=0; for(int i=1;i<len;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(p-1));
for(int i=lf;i<=mid;i++) a[i-lf+1]=f[i];
for(int i=1;i<=rg-lf;i++) b[i]=fac[i];
NTT(a,1,len); NTT(b,1,len);
for(int i=0;i<len;i++) a[i]=(1LL*a[i]*b[i])%mod;
NTT(a,-1,len);
for(int i=mid+1;i<=rg;i++) f[i]=(f[i]+a[i-lf+1])%mod;
solve(mid+1,rg);
}
int main()
{
fac[0]=1; for(int i=1;i<=100000;i++) fac[i]=(1LL*fac[i-1]*i)%mod;
for(int i=1;i<MXN;i<<=1) w[i]=modpow(G,(mod-1)/i),_w[i]=modpow(w[i],mod-2),iv[i]=modpow(i,mod-2);
solve(1,100000);
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m); bool flag=0; int ret=1;
for(int i=1,mx,mn,sz,x;i<=m;i++)
{
scanf("%d",&sz); mx=0; mn=inf;
for(int j=1;j<=sz;j++) {scanf("%d",&x); mn=min(mn,x); mx=max(mx,x);}
if(mx-mn+1!=sz) flag=1;
ret=(1LL*ret*f[sz])%mod;
}
if(flag) printf("0\n");
else printf("%d\n",ret);
}
return 0;
}