ZOJ 3874 Permutation Graph (分治NTT优化DP)
题目大意:给你一个排列,如果两个数构成了逆序对,就在他们之间连一条无向边,这样很多数会构成一个联通块。现在给出联通块内点的编号,求所有可能的排列数
推来推去容易发现性质,同一联通块内的点一定是连续标号的,否则无解
然后我就不会了
好神的$NTT$优化$DP$啊
根据上面的性质,联通块之间是互不影响的,所以我们对每个联通块分别统计答案再相乘
定义$f[i]$表示$i$个点构成的合法联通块,可能的排列数
一个合法联通块的所有元素一定在同一联通块内,说明不可能存在两个联通块,因此
$f[i]=i!-\sum f[j]*(i-j)!$
发现这是一个卷积的形式,用分治$NTT$求解即可
模数是一个原根是10的费马素数
别忘了判断无解的情况
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define N1 (1<<18)+10 6 #define il inline 7 #define dd double 8 #define ld long double 9 #define ll long long 10 using namespace std; 11 12 const int inf=0x3f3f3f3f; 13 const ll p=786433; 14 int gint() 15 { 16 int ret=0,fh=1;char c=getchar(); 17 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 18 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 19 return ret*fh; 20 } 21 ll qpow(ll x,ll y) 22 { 23 ll ans=1; 24 for(;y;x=x*x%p,y>>=1) if(y&1) ans=ans*x%p; 25 return ans; 26 } 27 28 namespace NTT{ 29 30 ll a[N1],b[N1],c[N1],invwn[N1],mulwn[N1]; 31 int r[19][N1]; 32 void Pre(int len,int L) 33 { 34 int i,j; 35 for(j=1;j<=L;j++) for(i=0;i<(1<<j);i++) 36 r[j][i]=(r[j][i>>1]>>1)|((i&1)<<(j-1)); 37 for(i=2;i<=len;i<<=1) mulwn[i]=qpow(10,(p-1)/i), invwn[i]=qpow(mulwn[i],p-2); 38 } 39 void NTT(ll *s,int len,int type,int L) 40 { 41 int i,j,k; ll wn,w,t; 42 for(i=0;i<len;i++) if(i<r[L][i]) swap(s[i],s[r[L][i]]); 43 for(k=2;k<=len;k<<=1) 44 { 45 wn=(type>0)?mulwn[k]:invwn[k]; 46 for(i=0;i<len;i+=k) 47 { 48 for(j=0,w=1;j<(k>>1);j++,w=w*wn%p) 49 { 50 t=w*s[i+j+(k>>1)]%p; 51 s[i+j+(k>>1)]=(s[i+j]+p-t)%p; 52 s[i+j]=(s[i+j]+t)%p; 53 } 54 } 55 } 56 } 57 void Main(int len,int L) 58 { 59 int i,invl=qpow(len,p-2); 60 NTT(a,len,1,L); NTT(b,len,1,L); 61 for(i=0;i<len;i++) c[i]=a[i]*b[i]%p; 62 NTT(c,len,-1,L); 63 for(i=0;i<len;i++) c[i]=c[i]*invl%p; 64 } 65 void clr(int sz) 66 { 67 memset(a,0,sz<<3); 68 memset(b,0,sz<<3); 69 } 70 71 }; 72 73 using NTT::a; using NTT::b; using NTT::c; 74 ll f[N1],g[N1]; int de; 75 void CDQ(int l,int r) 76 { 77 if(r-l<1) return; 78 if(r-l==1){ f[l]=(g[l]+p-f[l])%p; return; } 79 int mid=(l+r)>>1,i,len,L; 80 CDQ(l,mid); 81 for(len=1,L=0;len<(mid-l)+(r-l)-1;len<<=1,L++); 82 for(i=l;i<mid;i++) NTT::a[i-l]=f[i]; 83 for(i=0;i<(r-l);i++) NTT::b[i]=g[i]; 84 NTT::Main(len,L); 85 for(i=mid;i<r;i++) f[i]=(f[i]+NTT::c[i-l])%p; 86 NTT::clr(len); 87 CDQ(mid,r); 88 } 89 int T,n,m; 90 int que[N1]; 91 92 int main() 93 { 94 int i,j,x,y,len,L,mi,ma; ll ans; 95 scanf("%d",&T); n=100001; 96 for(i=2,g[1]=1;i<n;i++) g[i]=g[i-1]*i%p; 97 for(len=1,L=0;len<n+n-1;len<<=1,L++); 98 NTT::Pre(len,L); 99 CDQ(0,n); 100 101 while(T--){ 102 103 n=gint(); m=gint(); ans=1; 104 for(i=1;i<=m;i++) 105 { 106 x=gint(); mi=inf,ma=0; 107 for(j=1;j<=x;j++) que[j]=gint(), mi=min(mi,que[j]), ma=max(ma,que[j]); 108 if(ma-mi+1!=x) ans=0; 109 ans=(ans*f[x])%p; 110 } 111 printf("%lld\n",ans); 112 113 } 114 return 0; 115 116 }