题目链接:http://www.tsinsen.com/A1493
分析:就是论文里那道题,之前用cdq分治写过一次,学了论文之后,发现多项式也是可以进行各种神奇的操作的。
对于这道题来说
令
F[i]
为i个点的答案,
G[i]
为i个点任意连边的方案数,那么有
G[n]=∑i=1nF[i]∗G[n−i]∗Ci−1n−1
化简一步得
G[n](n−1)!=∑i=1nF[i](i−1)!G[n−i](n−i)!
右边显然是一个卷积,令
P(x)=∑i=1∞G[i](i−1)!∗xi
Q(x)=∑i=0∞G[i]i!∗xi
R(x)=∑i=0∞F[i](i−1)!∗xi
则有
P(x)=Q(x)∗R(x)
而我们要求的就是 R(x) 的每一项前的系数,而 P(x) 和 Q(x) 是已知的,于是我们只要求处 Q(x) 的逆元 Q−1x ,然后等式两边乘以 Q−1(x) 即可。
对 Q(x) 求逆元就是说找到另一个多项式 Q−1(x) ,满足
Q(x)∗Q−1(x)≡1modxn
其中n为 Q(x) 的阶.可以看出 Q−1(x) 的阶也是n
求逆元的核心在于
假如已经求出 Q−1(x) 使得 Q(x)∗Q−1(x)−1≡0modxn ,左侧是一个前n项都为0的多项式,我们把他平方,那么他前2n项也必然都为0,因此我们有
(Q(x)∗Q−1(x)−1)2≡0modx2n
化简一下就得到
(2Q−1(x)−Q(x)∗Q−1(x)2)∗P(x)≡1modx2n
于是就得到了mod x2n 的逆元,一直做下去,就可以得到要求的,复杂度为 T(n)=T(n/2)+O(nlogn) ,因此 T(n)=O(nlogn)
用类似的方法还可以求多项式的平方根
#include<bits/stdc++.h>
using namespace std;
const int M=1004535809,g=3;
typedef long long Int;
const int Maxn=262148;
Int rev2=(M+1)>>1;
Int a[Maxn],b[Maxn],c[Maxn],d[Maxn],G[Maxn],tp1[Maxn],tp2[Maxn],tp3[Maxn];
Int fac[Maxn],revf[Maxn],GG[Maxn];
Int powmod(Int x,Int y,Int mod)
{
Int ret=1,t=x;
while(y){if(y&1)ret=ret*t%mod;y>>=1;t=t*t%mod;}
return ret;
}
void rev(Int *a,int n)
{
int i,j,k;
for(i=1,j=n>>1;i<n-1;i++)
{
if(i<j)swap(a[i],a[j]);
for(k=n>>1;j>=k;j-=k,k>>=1);j+=k;
}
}
void dft(Int *a,int n,int flag=1)
{
rev(a,n);
for(int m=2;m<=n;m<<=1)
{
Int wm=powmod(g,(M-1)/m,M);
if(flag<0)wm=powmod(wm,M-2,M);
for(int k=0;k<n;k+=m)
{
Int w=1;
for(int j=k;j<k+(m>>1);j++,w=w*wm%M)
{
Int u=a[j],v=a[j+(m>>1)]*w%M;
a[j]=(u+v)%M;
a[j+(m>>1)]=(u-v+M)%M;
}
}
}
}
void mul(Int *a,Int *b,int n)
{
dft(a,n);dft(b,n);
for(int i=0;i<n;i++)a[i]=a[i]*b[i]%M;
dft(a,n,-1);
int revn=powmod(n,M-2,M);
for(int i=0;i<n;i++)a[i]=a[i]*revn%M;
}
void polrev(Int *a,Int *b,Int *c,Int *d,int n)//把a变成a的%(x^n)的逆元,要求a[0]!=0
{
for(int i=0;i<(n<<1);i++)b[i]=c[i]=d[i]=0;
b[0]=powmod(a[0],M-2,M);
for(int m=2;m<=n;m<<=1)
{
for(int j=0;j<m;j++)c[j]=d[j]=b[j];
mul(d,c,m);
for(int j=0;j<m;j++)c[j]=a[j];
mul(d,c,m<<1);
for(int j=0;j<m;j++)b[j]=(b[j]*2%M-d[j]+M)%M;
}
for(int i=0;i<n;i++)a[i]=b[i];
}
void polsqrt(Int *a,Int *b,Int *c,Int *d,int n)//把a变成sqrt(a)
{
for(int i=0;i<(n<<1);i++)b[i]=c[i]=d[i]=0;
b[0]=1;//b[0]=sqrt(a[0])
for(int m=2;m<=n;m<<=1)
{
for(int j=0;j<m;j++)c[j]=b[j];
polrev(c,tp1,tp2,tp3,m);
for(int j=0;j<m;j++)d[j]=a[j];
mul(d,c,m<<1);
for(int j=0;j<m;j++)b[j]=((b[j]+d[j])%M)*rev2%M;
}
for(int i=0;i<n;i++)a[i]=b[i];
}
int main()
{
int n=131072;
revf[0]=revf[1]=fac[0]=fac[1]=1;
for(int i=2;i<n;i++)revf[i]=revf[M%i]*(M-M/i)%M,fac[i]=fac[i-1]*i%M;
for(int i=2;i<n;i++)revf[i]=revf[i-1]*revf[i]%M;
for(int i=0;i<n;i++)GG[i]=powmod(2,(Int)i*(i-1)/2%(M-1),M);
for(int i=0;i<n;i++)a[i]=GG[i]*revf[i]%M;
for(int i=0;i<n;i++)G[i]=i*a[i]%M;
polrev(a,b,c,d,n);
mul(G,a,n<<1);
for(int i=1;i<n;i++)G[i]=G[i]*fac[i-1]%M;
while(scanf("%d",&n)!=EOF)printf("%lld\n",G[n]);
}