ZOJ 3874 Permutation Graph ——分治 NTT

发现每一块一定是按照一定的顺序的。

然后与标号无关,并且相同大小的对答案的影响相同。

然后列出递推式,上NTT+分治就可以了。

然后就可以与输入同阶处理答案了。

#include <map>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define D(i,j,k) for (int i=j;i>=k;--i)
#define ll long long
#define mp make_pair
#define maxn (1<<18)
#define md 786433
#define g 10

int dp[maxn],fac[maxn],A[maxn],B[maxn],t,n,m,rev[maxn],C[maxn];

int ksm(int a,int b)
{
	int ret=1;
	for(;b;b>>=1,a=(ll)a*a%md)if(b&1)ret=(ll)ret*a%md;
	return ret;
}

void NTT(int * x,int n,int flag)
{
	F(i,0,n-1) if (rev[i]>i) swap(x[rev[i]],x[i]);
	for (int m=2;m<=n;m<<=1)
	{
		int mid=m>>1,wn=ksm(10,((md-1)/m*flag+md-1)%(md-1));
		for (int i=0;i<n;i+=m)
		{
			int w=1;
			for (int j=0;j<mid;++j)
			{
				int u=x[i+j],v=(ll)x[i+j+mid]*w%md;
				x[i+j]=(u+v)%md;x[i+j+mid]=((u-v)%md+md)%md;
				w=(ll)w*wn%md;
			}
		}
	}
	if (flag==-1)
	{
		int inv=ksm(n,md-2);
		F(i,0,n-1) x[i]=(ll)x[i]*inv%md;
	}
}

void CDQ(int l,int r)
{
	if(l==r)return;int mid=l+r>>1;CDQ(l,mid);
	int n=mid-l+r-l,m=1;while((1<<m)<n)++m;n=(1<<m);
	F(i,0,n-1)A[i]=B[i]=0;
	F(i,l,mid)A[i-l]=dp[i];F(i,1,r-l) B[i-1]=fac[i];
	F(i,1,n-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<(m-1));
	NTT(A,n,1);NTT(B,n,1);
	F(i,0,n-1)C[i]=(ll)A[i]*B[i]%md;NTT(C,n,-1);
	F(i,mid+1,r)dp[i]=((dp[i]-C[i-l-1])%md+md)%md;
	CDQ(mid+1,r);
}

int main()
{
	fac[0]=1; F(i,1,100000) fac[i]=(ll)fac[i-1]*i%md;
	dp[0]=0; F(i,1,100000) dp[i]=fac[i];
	CDQ(1,100000);
	scanf("%d",&t);
	while(t--)
	{
		int ans=1,k,x;
		scanf("%d%d",&n,&m);
		F(i,1,m)
		{
			int mn=n+1,mx=-1;
			scanf("%d",&k); ans=(ll)ans*dp[k]%md;
			F(j,1,k) scanf("%d",&x),mn=min(mn,x),mx=max(mx,x);
			if (k!=mx-mn+1) ans=0;
		}
		printf("%d\n",ans);
	}
}

  

posted @ 2017-04-27 21:14  SfailSth  阅读(202)  评论(0编辑  收藏  举报