zoukankan      html  css  js  c++  java
  • bzoj 3456 城市规划——分治FFT / 多项式求逆 / 多项式求ln

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3456

    分治FFT:

      设 dp[ i ] 表示 i 个点时连通的方案数。

      考虑算补集:连通的方案数 == 随便连方案数 - 不连通方案数

      不连通方案数就和很久之前做过的“地震后的幻想乡”一样,枚举一个连通的点集,其中需要一直包含一个“划分点”保证不重复;其余部分随便连。注意还有从 i 个点里选 j 个点作为连通点集的那个组合数。

      ( dp[i]=2^{C^{2}_{i}} - sumlimits^{i-1}_{j=1} dp[j]*C^{j-1}_{i-1}*2^{C^{2}_{i-j}} )

      ( dp[i]=2^{C^{2}_{i}} - (i-1)!sumlimits^{i-1}_{j=1} ( dp[j]*frac{1}{(j-1)!} )( 2^{C^{2}_{i-j}}*frac{1}{(i-j)!} ) )

      就可以分治FFT啦!

      一开始还记得指数是对 mod-1 取模,后来就忘了,调了好久……注意 mod-1 不是质数,且是偶数,所以2没有逆元,算 C 的时候直接 /2 就行了。

      在 L==R 的地方把 f [ ] 弄好。虽然也可以给 f 赋上 ( C^{2}_{i} ) 的初值,然后卷积的时候每次 -=( (i-1)!*a[j] ),就不用在 L==R 的时候特殊判断了,但那样可能因为总要乘 ( (i-1)! ),会变慢。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=130005,M=N<<1,mod=1004535809,m2=mod-1;//N<<1
    int n,len,r[M],f[M],g[M],a[M],b[M],jc[N],jcn[N],inv2;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    void upd(int &x){x>=mod?x-=mod:0;}
    int pw(int x,int k,int md=mod)
    {int ret=1;while(k){if(k&1)ret=(ll)ret*x%md;x=(ll)x*x%md;k>>=1;}return ret;}
    int C(int n)///////%mod-1////
    {return (ll)n*(n-1)/2%m2;}// /2
    void init()
    {
      //////  inv2=pw(2,m2-2,m2);//////m2!!!! m2 is not prime!!!!!
      jc[1]=1;for(int i=2;i<n;i++)jc[i]=(ll)jc[i-1]*i%mod;
      jcn[n-1]=pw(jc[n-1],mod-2);
      for(int i=n-2;i>=0;i--)jcn[i]=(ll)jcn[i+1]*(i+1)%mod;//>=0
      for(int i=1;i<n;i++)g[i]=(ll)pw(2,C(i))*jcn[i]%mod;
    }
    void ntt(int *a,bool fx)
    {
      for(int i=0;i<len;i++)
        if(i<r[i])swap(a[i],a[r[i]]);
      for(int R=2;R<=len;R<<=1)
        {
          int Wn=pw( 3,fx?(mod-1)-(mod-1)/R:(mod-1)/R );
          for(int i=0,m=R>>1;i<len;i+=R)
        for(int j=0,w=1;j<m;j++,w=(ll)w*Wn%mod)
          {
            int x=a[i+j], y=(ll)w*a[i+m+j]%mod;
            a[i+j]=x+y;  upd(a[i+j]);
            a[i+m+j]=x+mod-y;  upd(a[i+m+j]);
          }
        }
      if(!fx)return; int inv=pw(len,mod-2);
      for(int i=0;i<len;i++)a[i]=(ll)a[i]*inv%mod;
    }
    void solve(int L,int R)
    {
      if(L==R){f[L]=(pw(2,C(L))-(ll)jc[L-1]*f[L])%mod+mod;upd(f[L]);return;}
      int mid=L+R>>1;  solve(L,mid);
      int d=R-L,i,j;
      for(len=1;len<d;len<<=1);
      for(i=0;i<len;i++)r[i]=(r[i>>1]>>1)+((i&1)?len>>1:0);
    
      for(i=0,j=L;j<=mid;i++,j++)a[i]=(ll)f[j]*jcn[j-1]%mod;//jcn
      for(;i<len;i++)a[i]=0;
      for(i=0,j=R-L;i<=j;i++)b[i]=g[i+1];
      for(;i<len;i++)b[i]=0;
      ntt(a,0); ntt(b,0);
      for(i=0;i<len;i++)a[i]=(ll)a[i]*b[i]%mod;
      ntt(a,1);
      for(int i=mid+1,j=i-L-1;i<=R;i++,j++)f[i]+=a[j],upd(f[i]);
      solve(mid+1,R);
    }
    int main()
    {
      n=rdn(); init();
      solve(1,n);
      printf("%d
    ",f[n]);
      return 0;
    }
    View Code

     多项式求逆:

      http://blog.miskcoo.com/2015/05/bzoj-3456

      应该只有G(x)有0次项。在阶乘的地方看,0!应该等于1才对(预处理组合数时就是这样,想一想,( C^{n}_{n} )就是( frac{n!}{n!*0!} ))。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=1e5+5,M=N<<2,mod=1004535809;
    int n,a[M],c[M],cn[M],A[M],jcn[M],len,r[M];
    void upd(int &x){x>=mod?x-=mod:0;}
    int C(int n){return (ll)n*(n-1)/2%(mod-1);}
    int pw(int x,int k)
    {int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;}
    void ntt(int *a,bool fx)
    {
      for(int i=0;i<len;i++)
        if(i<r[i])swap(a[i],a[r[i]]);
      for(int R=2;R<=len;R<<=1)
        {
          int Wn=pw( 3,fx?(mod-1)-(mod-1)/R:(mod-1)/R );
          for(int i=0,m=R>>1;i<len;i+=R)
        for(int j=0,w=1;j<m;j++,w=(ll)w*Wn%mod)
          {
            int x=a[i+j], y=(ll)w*a[i+m+j]%mod;
            a[i+j]=x+y; upd(a[i+j]);
            a[i+m+j]=x+mod-y; upd(a[i+m+j]);
          }
        }
      if(!fx)return ; int inv=pw( len,mod-2 );
      for(int i=0;i<len;i++)a[i]=(ll)a[i]*inv%mod;
    }
    void getinv(int n,int *a,int *b)
    {
      if(n==1){b[0]=1;return;}
      getinv(n+1>>1,a,b);
      int i;
      for(len=1;len<n<<1;len<<=1);
      for(i=0;i<len;i++)r[i]=(r[i>>1]>>1)+((i&1)?len>>1:0);
      for(i=0;i<n;i++)A[i]=a[i];  for(;i<len;i++)A[i]=0;
      ntt(A,0);  ntt(b,0);
      for(i=0;i<len;i++)b[i]=(2-(ll)A[i]*b[i]%mod)*b[i]%mod+mod,upd(b[i]);
      ntt(b,1);
      for(int i=n;i<len;i++)b[i]=0;
    }
    int main()
    {
      scanf("%d",&n);
      for(int i=1;i<=n;i++)a[i]=pw(2,C(i));
      jcn[0]=1;for(int i=1;i<=n;i++)jcn[i]=(ll)jcn[i-1]*i%mod;
      jcn[n]=pw(jcn[n],mod-2);for(int i=n-1;i>=0;i--)jcn[i]=(ll)jcn[i+1]*(i+1)%mod;
      c[0]=1; for(int i=1;i<=n;i++)c[i]=(ll)a[i]*jcn[i]%mod;
      for(int i=1;i<=n;i++)a[i]=(ll)a[i]*jcn[i-1]%mod;
      getinv(n+1,c,cn);
      for(len=1;len<=n<<1;len<<=1);
      for(int i=0;i<len;i++)r[i]=(r[i>>1]>>1)+((i&1)?len>>1:0);
      ntt(a,0); ntt(cn,0);
      for(int i=0;i<len;i++)a[i]=(ll)a[i]*cn[i]%mod;
      ntt(a,1);
      printf("%lld
    ",(ll)a[n]*pw(jcn[n-1],mod-2)%mod);
      return 0;
    }
    View Code

    多项式求 ln :

    关于指数型生成函数:https://max.book118.com/html/2015/1111/29175835.shtm

    想求两种物品一共取 i 个的排列方案,设取第一种物品的方案是 f[ ] ,第二种物品的方案是 g[ ] ,那么就是想求

      ( sumlimits_{j=0}^{i}C_{i}^{j}f[i]*g[i-j] )

    令 F(x) 是 f[ ] 的指数型生成函数、G(x) 是 g[ ] 的生成函数的话,要求的就恰好是 F(x) * G(x) 的第 i 次项系数乘上 i! 。

    关于本题:https://www.cnblogs.com/ivorysi/p/9756961.html

    至于为什么式子 ( G(x)=sumlimits_{i=1}^{infty}frac{F(i)}{i!} ) 里要除以 i 的阶乘,可以从 n=2 并希望有两个连通块的情况考虑:

    令 ( g[ i ] = 2^{C_{n}^{2}} ) 表示 i 个点的无向图个数,这个算出来是有标号的;令 f[ i ] 表示 i 个点有标号无向连通图个数。

    ( F^{2}(x) = sumlimits_{i=0}^{n}frac{ sumlimits_{j=0}^{i}C_{i}^{j}f[j]*f[i-j] }{i!}x^i )

    直接这样的话,就有 ( g[2]=C_{2}^{0}*f[0]*f[2]+C_{2}^{1}*f[1]*f[1]+C_{2}^{2}f[2]*f[0] )

    可以发现,因为有了那个 C 的系数,所以即使是卷积的时候只会算一遍的 j == i-j 的情况,也会有重复;即那个博客里说的本质一样的情况。

    关于求ln:http://www.cnblogs.com/zhoushuyu/p/8763215.html

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=(1<<19)+5,mod=1004535809;
    int upt(int x){while(x>=mod)x-=mod; while(x<0)x+=mod; return x;}
    int pw(int x,int k)
    {int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;}
    
    int n,len,r[N],f[N],g[N],A[N],B[N];
    int jc[N],jcn[N],inv[N];
    void init(int n)
    {
      jc[0]=jcn[0]=inv[0]=1;
      jc[1]=jcn[1]=inv[1]=1;
      for(int i=2;i<=n;i++)
        {
          jc[i]=(ll)jc[i-1]*i%mod;
          inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
          jcn[i]=(ll)jcn[i-1]*inv[i]%mod;
        }
    }
    void ntt(int *a,bool fx,bool deb=0)
    {
      for(int i=0;i<len;i++)
        if(i<r[i])swap(a[i],a[r[i]]);
      for(int R=2;R<=len;R<<=1)
        {
          int wn=pw( 3,fx?(mod-1)-(mod-1)/R:(mod-1)/R );
          for(int i=0,m=R>>1;i<len;i+=R)
        for(int j=0,w=1;j<m;j++,w=(ll)w*wn%mod)
          {
            int x=a[i+j],y=(ll)w*a[i+m+j]%mod;
            a[i+j]=upt(x+y); a[i+m+j]=upt(x-y);
          }
        }
      if(!fx)return; int inv=pw(len,mod-2);
      for(int i=0;i<len;i++)a[i]=(ll)a[i]*inv%mod;
    }
    void get_dao(int n,int *a,int *b)
    {
      for(int i=1;i<n;i++)b[i-1]=(ll)a[i]*i%mod;
      b[n]=b[n-1]=0;
    }
    void get_jf(int n,int *a)
    {
      for(int i=n-1;i;i--)a[i]=(ll)a[i-1]*inv[i]%mod;//i--
      a[0]=0;
    }
    void get_inv(int n,int *a,int *b)
    {
      if(n==1){b[0]=pw(a[0],mod-2);return;}
      get_inv(n>>1,a,b);
      len=n<<1;
      for(int i=0,j=len>>1;i<len;i++)
        r[i]=(r[i>>1]>>1)+((i&1)?j:0);
      for(int i=0;i<n;i++)A[i]=a[i],A[i+n]=0;
      ntt(A,0); ntt(b,0);
      for(int i=0;i<len;i++)
        b[i]=(ll)b[i]*(2-(ll)A[i]*b[i]%mod+mod)%mod;
      ntt(b,1); for(int i=n;i<len;i++)b[i]=0;
    }
    void get_ln(int n,int *a,int *b)
    {
      get_inv(n,a,B); get_dao(n,a,A);//get_inv will use A
      len=n<<1; for(int i=n;i<len;i++)A[i]=0;//
      for(int i=0,j=len>>1;i<len;i++)
        r[i]=(r[i>>1]>>1)+((i&1)?j:0);
      ntt(A,0,1); ntt(B,0,1);
      for(int i=0;i<len;i++)b[i]=(ll)A[i]*B[i]%mod;
      ntt(b,1);
      get_jf(n,b);
      for(int i=n;i<len;i++)b[i]=0;//
    }
    int main()
    {
      scanf("%d",&n);int l;for(l=1;l<=n;l<<=1);//l>n
      init(l);
      g[0]=1;///
      for(int i=1;i<=n;i++)
        g[i]=(ll)pw(2,(ll)i*(i-1)/2%(mod-1))*jcn[i]%mod;
      get_ln(l,g,f); printf("%lld
    ",(ll)f[n]*jc[n]%mod);
      return 0;
    }
    View Code
  • 相关阅读:
    OpenCV学习:图片的读取、展示
    Windows下使用Anaconda配置opencv和tensorflow环境
    二叉树的链式存储的实现以及对二叉树的各种操作
    数据结构——树笔记1
    Python学习笔记整理总结【Django】Ajax
    Python学习笔记整理总结【Django】:中间件、CSRF、缓存
    Python学习笔记整理总结【Django】:模板语言、分页、Cookie、Session
    Python学习笔记整理总结【Django】:Model操作(一)
    Python学习笔记整理总结【Django】【MVC/MTV/路由分配系统(URL)/视图函数 (views)/表单交互】
    数据结构与算法(C/C++版)【树与二叉树】
  • 原文地址:https://www.cnblogs.com/Narh/p/10046287.html
Copyright © 2011-2022 走看看