一、题目
Input:
给出 n个点(n个点是互异的),求出满足下列条件的连边方案:
- 不存在重边和自环。
- 不存在三个点a,b,c使三个点间两两距离相等。
Output:
一个整数n,表示方案数对
(
,质数)取模的结果。
二、解法
本题最重要的条件是:不存在三个点a,b,c使三个点间两两距离相等
,这告诉我们图中不能存在度数
的点,同样也不能存在长度为
的倍数的环,所以图中就只能有链和长度不为
的环。
我们分别考虑它们的指数型生成函数(EGF),为什么要用EGF呢?是因为
个点互异,在链和环的内部我们会考虑顺序,所以要除去内部的排列,但在外面我们是需要做一个全排列来确定顺序,问题就很像多重集的排列,所以我们使用EGF。长度为
的链有
中,环有
种,所以它们的生成函数长这样:
要注意
的一次项为
,
在次数等于
时值为
。
由于整张图是由 的并集拼出来的,所以答案的生成函数是 ,这里需要用到多项式的 。
解释一下上面那句话,答案的多项式应该是
,也就是环和链都是能无限选的,为什么要用
呢?我们可以把它拆开,也就是
,我们把上面的柿子泰勒展开,也就是把
当做
:
那么我们为什么要带上
呢?这就要从这个条件:n个点是互异的
讲起,请看下图:
计算的过程应该是先求
个数的排列,再分成链和环,每个链和环内部交给它自己排列,所以要用 EGF 除去外面给它的排列,然后相同长度的环和链之间是可以交换的,所以要除去它们的排列,这就是带上
原因,不仅如此,
还能保证无限选,所以我们用多项式
。
最后补充一点,由于此题的 实在是太大了,用 时如果求两个长度为 多项式的卷积,算长度只能这么算:
len=1;while(len<2*n) len<<=1;
而不能这么算:
len=1;while(len<=2*n) len<<=1;
因为加上等于号,长度就可能超过 次方,原根的处理就会出错,最大的数据点会过不了。
#include <cstdio>
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int MAXN = 3000005;
const int MOD = 1004535809;
int read()
{
int num=0,flag=1;
char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,len,lg,Rev[MAXN],A[MAXN],B[MAXN],C[MAXN],D[MAXN];
int a[MAXN],b[MAXN],c[MAXN],fac[MAXN];
void init(int n)
{
fac[0]=1;
for(int i=1; i<=n; i++) fac[i]=fac[i-1]*i%MOD;
}
int qkpow(int a,int b)
{
int res=1;
while(b>0)
{
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
void NTT(int *a,int len,int tmp)
{
lg=(int)round(log2(len));
for(int i=0; i<len; i++)
{
Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
if(i<Rev[i])
swap(a[i],a[Rev[i]]);
}
for(int s=2; s<=len; s<<=1)
{
int t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s):qkpow(3,(MOD-1)-(MOD-1)/s);
for(int i=0; i<len; i+=s)
{
int x=1;
for(int j=0; j<t; j++,x=x*w%MOD)
{
int fe=a[i+j],fo=a[i+j+t];
a[i+j]=(fe+x*fo)%MOD;
a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
}
}
}
if(tmp==1) return ;
int inv=qkpow(len,MOD-2);
for(int i=0; i<len; i++)
a[i]=a[i]*inv%MOD;
}
void work(int n,int *a,int *b)
{
len=1;
while(len<2*n) len<<=1;
for(int i=0; i<len; i++) A[i]=B[i]=0;
for(int i=0; i<n; i++) A[i]=a[i];
for(int i=0; i<n; i++) B[i]=b[i];
NTT(A,len,1);
NTT(B,len,1);
for(int i=0; i<len; i++) A[i]=2*B[i]-B[i]*B[i]%MOD*A[i]%MOD,A[i]=(A[i]%MOD+MOD)%MOD;
NTT(A,len,-1);
for(int i=0; i<n; i++) b[i]=(A[i]%MOD+MOD)%MOD;
}
void inv(int n,int *a,int *b)
{
int cur=1;
for(int i=0; i<n; i++) b[i]=0;
b[0]=qkpow(a[0],MOD-2);
while(cur<n)
{
cur<<=1;
work(cur,a,b);
}
}
void mul(int n,int *a,int *b,int *c)
{
len=1;
while(len<2*n) len<<=1;
for(int i=0; i<len; i++) A[i]=B[i]=0;
for(int i=0; i<n; i++) A[i]=a[i];
for(int i=0; i<n; i++) B[i]=b[i];
NTT(A,len,1);
NTT(B,len,1);
for(int i=0; i<len; i++) A[i]=A[i]*B[i]%MOD;
NTT(A,len,-1);
for(int i=0; i<n; i++)
c[i]=A[i];
}
void ln(int n,int *a,int *b)
{
for(int i=0; i<n; i++)
C[i]=a[i];
inv(n,C,D);
for(int i=0; i<n; i++)
C[i]=C[i+1]*(i+1)%MOD;
mul(n,C,D,C);
b[0]=0;
for(int i=1; i<n; i++)
b[i]=C[i-1]*qkpow(i,MOD-2)%MOD;
}
void exp(int n,int *a,int *b)
{
int cur=1;
b[0]=1;
while(cur<n)
{
cur<<=1;
ln(cur,b,c);
for(int i=0; i<cur; i++)
c[i]=a[i]-c[i];
c[0]++;
len=1;
while(len<2*cur) len<<=1;
NTT(b,len,1);
NTT(c,len,1);
for(int i=0; i<len; i++) b[i]=b[i]*c[i]%MOD;
NTT(b,len,-1);
}
}
signed main()
{
n=read();
init(n);
a[1]=1;
int inv2=qkpow(2,MOD-2);
for(int i=2; i<=n; i++) a[i]=inv2;
for(int i=4; i<=n; i++) if(i%3) a[i]=(a[i]+qkpow(2*i,MOD-2))%MOD;
exp(n+1,a,b);
printf("%lld\n",b[n]*fac[n]%MOD);
}