fft,ntt总结

a 夏天 提交于 2019-12-17 06:46:48

一个套路:把式子推成卷积形式,然后用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)}$则答案即为$\sum\limits_{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\n",i,sum[i]);
107         ans+=1.0*sum[i]/(i+1.0);
108     }
109     printf("%.4lf\n",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+二项式反演。首先化式子,$ \binom{m}{k}*\binom{n}{k*s}*\frac{(k*s)!}{x^{k}}*(m-k)^{n-k*s} $就得到了钦定k个的方案数,注意,是钦定,而不是至少。

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

T9:城市规划

次题考察分治fft,分治fft的形式:$f[i]=\sum\limits_{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\n",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\n",1ll*f[i]*inc[i-1]%mod);
65     printf("%lld\n",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]-\sum\limits_{j=1}^{i-1}\binom{i-1}{j}g[i-j]*f[j]$就可以卷积了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!