gxx_slide之城市规划2

题目链接:http://www.tsinsen.com/A1493
分析:就是论文里那道题,之前用cdq分治写过一次,学了论文之后,发现多项式也是可以进行各种神奇的操作的。
对于这道题来说
F[i] 为i个点的答案, G[i] 为i个点任意连边的方案数,那么有

G[n]=i=1nF[i]G[ni]Ci1n1

化简一步得
G[n](n1)!=i=1nF[i](i1)!G[ni](ni)!

右边显然是一个卷积,令
P(x)=i=1G[i](i1)!xi

Q(x)=i=0G[i]i!xi

R(x)=i=0F[i](i1)!xi

则有
P(x)=Q(x)R(x)

而我们要求的就是 R(x) 的每一项前的系数,而 P(x) Q(x) 是已知的,于是我们只要求处 Q(x) 的逆元 Q1x ,然后等式两边乘以 Q1(x) 即可。
Q(x) 求逆元就是说找到另一个多项式 Q1(x) ,满足
Q(x)Q1(x)1modxn

其中n为 Q(x) 的阶.可以看出 Q1(x) 的阶也是n
求逆元的核心在于
假如已经求出 Q1(x) 使得 Q(x)Q1(x)10modxn ,左侧是一个前n项都为0的多项式,我们把他平方,那么他前2n项也必然都为0,因此我们有
(Q(x)Q1(x)1)20modx2n

化简一下就得到
(2Q1(x)Q(x)Q1(x)2)P(x)1modx2n

于是就得到了mod x2n 的逆元,一直做下去,就可以得到要求的,复杂度为 T(n)=T(n/2)+O(nlogn) ,因此 T(n)=Onlogn)
用类似的方法还可以求多项式的平方根

#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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值