zoukankan      html  css  js  c++  java
  • fft,ntt总结

    一个套路:把式子推成卷积形式,然后用fft或ntt优化求解过程。

    fft的扩展性不强,不可以在fft函数里多加骚操作--DeepinC

    T1:多项式乘法

    板子题

    T2:快速傅立叶之二

    另一个板子,小技巧:把一个数组反转过来,以符合卷积形式

    T3:力

    拆式子,把qj除到左边,然后把大于j的贡献和小于j的贡献分开考虑,对于小于j的,直接用fft统计,对于大于的,先反转再fft

    T4:Normal

    大神题,考虑把贡献拆成点对,对于两个点i与j,若i能对j作出贡献,则i到j的路径上没有断点,同样删除i到j路径以外的点不影响i与j之间的贡献,则i对j作出贡献的概率为

    $frac{1}{dis(i,j)}$则答案即为$sumlimits_{i=1}^{n}sum limits_{j=1}^{n}frac{1}{dis(i,j)}$ 然后这玩意可以用点分治求,合并子树用fft优化

      1 #include<bits/stdc++.h>
      2 #define N 70050
      3 #define LL long long
      4 const int mod=998244353,G1=3,G2=(mod+1)/G1;
      5 #define cri const register int 
      6 using namespace std;
      7 int a[N],b[N];
      8 int n;
      9 int he[N],ne[N],to[N],tot;
     10 int sz[N],alsz,vis[N],rt;
     11 inline void addedge(int x,int y){to[++tot]=y;ne[tot]=he[x];he[x]=tot;}
     12 inline int dfssz(int g,int fa){
     13     sz[g]=1;int ret=1;
     14     for(int i=he[g],k;i;i=ne[i]){
     15         if(to[i]==fa||vis[to[i]])continue;
     16         k=dfssz(to[i],g);sz[g]+=sz[to[i]];ret=max(ret,k+1);
     17     }return ret;
     18 }
     19 inline void dfsrt(int g,int fa){
     20     int hs=0;
     21     for(int i=he[g];i;i=ne[i]){
     22         if(to[i]==fa||vis[to[i]])continue;
     23         dfsrt(to[i],g);hs=max(hs,sz[to[i]]);
     24     }
     25     if(sz[g]>(alsz>>1)&&hs<=(alsz>>1))rt=g;
     26 }
     27 
     28 inline int dfsdep(int g,int fa,int *a,int d){
     29     ++a[d];
     30     for(int i=he[g];i;i=ne[i]){
     31         if(to[i]==fa||vis[to[i]])continue;
     32         dfsdep(to[i],g,a,d+1);
     33     }
     34 }
     35 
     36 int r[N];
     37 
     38 inline int qpow(int d,int z)
     39 {
     40     int ret=1;
     41     for(;z;z>>=1,d=1ll*d*d%mod)
     42         if(z&1)ret=1ll*ret*d%mod;
     43     return ret;
     44 }
     45 inline void ntt(int *a,cri n,cri tag){
     46     int cnt=-2;for(int i=n;i;i>>=1,++cnt);
     47     for(int i=0;i<n;++i){
     48         r[i]=(r[i>>1]>>1)|((i&1)<<cnt);
     49         if(r[i]>i)swap(a[i],a[r[i]]);
     50     }
     51     for(int i=1;i<n;i<<=1){
     52         LL u=qpow(tag==1?G1:G2,(mod-1)/i/2),w=1,t;
     53         for(int j=0,I=i<<1;j<n;j+=I,w=1)
     54             for(int k=0;k<i;++k,w=w*u%mod)
     55                 t=a[j+k+i]*w%mod,a[j+k+i]=(a[j+k]-t+mod)%mod,(a[j+k]+=t)%=mod;
     56     }
     57 }
     58 int c[N];int sum[N];
     59 inline void work(int g)
     60 {
     61     int d=dfssz(g,0)-1,lim=1;
     62     vis[g]=1;if(sz[g]==1)return;
     63     for(lim=1;lim<=d+d;lim<<=1);
     64     for(register int i=0;i<lim;++i)a[i]=b[i]=0;
     65     for(int i=he[g],d2,l2;i;i=ne[i]){
     66         if(vis[to[i]])continue;dfsdep(to[i],g,a,1);
     67         for(d2=1;a[d2];++d2)b[d2]+=a[d2];--d2;
     68         for(l2=1;l2<=d2+d2;l2<<=1);
     69         
     70         ntt(a,l2,1);
     71         for(int j=0;j<l2;++j)a[j]=1ll*a[j]*a[j]%mod;
     72         ntt(a,l2,-1);
     73 
     74         const LL iv=qpow(l2,mod-2);
     75         for(int j=0;j<=d2+d2;++j)sum[j]-=iv*a[j]%mod;
     76         for(int j=0;j<l2;++j)a[j]=0;
     77     }
     78     
     79     for(int i=0;i<=d;++i)sum[i]+=b[i]<<1;
     80     ntt(b,lim,1);
     81     for(int i=0;i<lim;++i)b[i]=1ll*b[i]*b[i]%mod;
     82     ntt(b,lim,-1);
     83     const LL iv=qpow(lim,mod-2);
     84     for(int i=0;i<=d+d;++i)sum[i]+=iv*b[i]%mod;
     85     
     86     
     87     for(int i=he[g];i;i=ne[i]){
     88         if(vis[to[i]])continue;
     89         alsz=sz[to[i]],dfsrt(to[i],g),c[++c[0]]=rt;
     90     }
     91     
     92 }
     93 int main()
     94 {
     95 //    freopen("da.in","r",stdin);
     96     scanf("%d",&n);
     97     for(int i=1,x,y;i<n;++i){
     98         scanf("%d%d",&x,&y),++x,++y;
     99         addedge(x,y),addedge(y,x);
    100     }
    101     dfssz(1,0);alsz=sz[1];dfsrt(1,0);c[++c[0]]=rt;
    102     for(int i=1;i<=c[0];++i)work(c[i]);
    103     double ans=0;
    104     for(int i=1;i<n;++i)
    105     {
    106 //        printf("i:%d sum:%d
    ",i,sum[i]);
    107         ans+=1.0*sum[i]/(i+1.0);
    108     }
    109     printf("%.4lf
    ",ans+n);
    110 }
    View Code

    T5:万径人踪灭

    字符串与fft的结合,考虑以每个点为中心,用总共的减去连续的。发现只要求出以每个点为中心的对称的点对有多少个即可,对于中心mid,若l与r关于mid对称,

    则mid=(l+r)>>1,然后这个东西就可以卷积了。对a和b分别卷积,就能求出以每个点为中心的对称的点对的个数,求连续的个数可以manacher也可以hash+二分

    T6:序列统计

    考察原根的用法。利用原根把乘法转化为加法,即将1~p-1中的每个数都表示为原根的k次方,原数相乘=新数相加,然后就可以ntt了

    T7:求和

    fft与斯特林数结合,个人感觉难想的点其实在于把j的枚举范围从i直接升到n,这样其实是去掉j对i的限制,同时不影响答案。

    T8:染色

    fft+二项式反演。首先化式子,$ inom{m}{k}*inom{n}{k*s}*frac{(k*s)!}{x^{k}}*(m-k)^{n-k*s} $就得到了钦定k个的方案数,注意,是钦定,而不是至少。

    然后利用二项式反演的至少形式,化成卷积式,就可以fft了。

    T9:城市规划

    次题考察分治fft,分治fft的形式:$f[i]=sumlimits_{j=1}^{i}g[j]*f[i-j]$

     1 #include<bits/stdc++.h>
     2 #define cri const register int 
     3 #define N 270050
     4 #define LL long long
     5 const int mod=1004535809,G1=3,G2=(mod+1)/G1;
     6 using namespace std;
     7 int n,f[N],g[N];
     8 inline int qpow(int d,LL z){
     9     int ret=1;
    10     for(;z;z>>=1,d=1ll*d*d%mod)
    11         if(z&1)ret=1ll*ret*d%mod;
    12     return ret;
    13 }
    14 int inc[N],inv[N];
    15 inline void init(int n){
    16     inc[0]=inv[0]=1;
    17     for(int i=1;i<=n;++i)inc[i]=1ll*inc[i-1]*i%mod;
    18     inv[n]=qpow(inc[n],mod-2);
    19     for(int i=n-1;i;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod;
    20     for(int i=1;i<=n;++i)
    21         g[i]=1ll*qpow(2,1ll*i*(i-1)>>1)*inv[i]%mod;
    22 }
    23 inline void ntt(int *a,cri n,cri tag)
    24 {
    25     static int r[N],cnt;
    26     cnt=-2;for(int i=n;i;i>>=1,++cnt);
    27     for(int i=0;i<n;++i){
    28         r[i]=(r[i>>1]>>1)|((i&1)<<cnt);
    29         if(i>r[i])swap(a[i],a[r[i]]);
    30     }
    31     for(int i=1;i<n;i<<=1){
    32         LL u=qpow(tag==1?G1:G2,(mod-1)/i/2),w=1,t;
    33         for(int j=0,I=i<<1;j<n;j+=I,w=1)
    34             for(int k=0;k<i;++k,(w*=u)%=mod)
    35                 t=w*a[j+k+i]%mod,a[j+k+i]=(a[j+k]-t+mod)%mod,(a[j+k]+=t)%=mod;
    36     }
    37 }
    38 inline void solve(int l,int r){
    39     if(l>n)return;
    40     if(l==r){//printf("f[%d]=%d
    ",l,f[l]);
    41         if(l){
    42             f[l]=1ll*f[l]*inc[l-1]%mod;
    43             f[l]=(1ll*g[l]*inc[l]%mod+0ll+mod-f[l])%mod;
    44             f[l]=1ll*f[l]*inv[l-1]%mod;
    45         }
    46         return;
    47     }
    48     static int ff[N],gg[N];
    49     const int mid=l+r>>1;
    50     solve(l,mid);
    51     
    52     for(int i=l;i<=mid;++i)ff[i-l]=f[i],gg[i-l]=g[i-l];
    53     for(int i=mid+1;i<=r;++i)ff[i-l]=0,gg[i-l]=g[i-l];//gg[r-l]=0;
    54     ntt(ff,r-l+1,1);ntt(gg,r-l+1,1);
    55     for(int i=0;i<r-l+1;++i)ff[i]=1ll*ff[i]*gg[i]%mod;
    56     ntt(ff,r-l+1,-1);const LL iv=qpow(r-l+1,mod-2);
    57     for(int i=mid+1;i<=r;++i)(f[i]+=iv*ff[i-l]%mod)%=mod;
    58     solve(mid+1,r);
    59 }
    60 int main(){
    61     scanf("%d",&n);init(n);
    62     int lim;for(lim=1;lim<=n;lim<<=1);
    63     solve(0,lim-1);
    64 //    for(int i=1;i<=n;++i)printf("%lld
    ",1ll*f[i]*inc[i-1]%mod);
    65     printf("%lld
    ",1ll*f[n]*inc[n-1]%mod);
    66     return 0;
    67 }
    板子

    然而这题化式子好像也挺难的。。。

    考虑用总共的减去不合法的。设f[i]为答案数组,g[i]为i个点随意组合的方案数,则g[i]=2n*(n-1)/2

    考虑最后一个点加在了哪个联通块里面:$f[i]=g[i]-sumlimits_{j=1}^{i-1}inom{i-1}{j}g[i-j]*f[j]$就可以卷积了。

  • 相关阅读:
    http请求类型简介
    关于PLSQL连接报错:ORA-12154:TNS:无法解析指定的连接标识符
    JDK8的安装与配置
    今天新装tomcat遇到黑窗口(startup.bat)启动乱码问题解决!!!
    一个简单的工厂模式(一个接口,多个实现,通过调用条件的不同,分别去调用符合的实现)
    数组(复习)
    java选择结构、循环结构(复习)
    java常用的数据类型,变量和常量,运算符(复习)
    java输入输出,书写规范,运行原理,跨平台原理(复习)
    JAVA基础入门(JDK、eclipse下载安装)
  • 原文地址:https://www.cnblogs.com/loadingkkk/p/12051577.html
Copyright © 2011-2022 走看看