HDU5730 FFT+CDQ分治

题意:dp[n] = ∑ ( dp[n-i]*a[i] )+a[n], ( 1 <= i < n)

cdq分治。

计算出dp[l ~ mid]后,dp[l ~ mid]与a[1 ~ r-l]做卷积运算。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 4e5+5;
 4 const int mod = 313;
 5 const double pi = acos(-1.0);
 6 struct comp{
 7     double r,i;comp(double _r=0,double _i=0){r=_r;i=_i;}
 8     comp operator+(const comp x){return comp(r+x.r,i+x.i);}
 9     comp operator-(const comp x){return comp(r-x.r,i-x.i);}
10     comp operator*(const comp x){return comp(r*x.r-i*x.i,r*x.i+i*x.r);}
11 }x[N], y[N];
12 void FFT(comp a[],int n,int t){
13     for(int i=1,j=0;i<n-1;i++){
14         for(int s=n;j^=s>>=1,~j&s;);
15         if(i<j)swap(a[i],a[j]);
16     }
17     for(int d=0;(1<<d)<n;d++){
18         int m=1<<d,m2=m<<1;
19         double o=pi/m*t;comp _w(cos(o),sin(o));
20         for(int i=0;i<n;i+=m2){
21             comp w(1,0);
22             for(int j=0;j<m;j++){
23                 comp &A=a[i+j+m],&B=a[i+j],t=w*A;
24                 A=B-t;B=B+t;w=w*_w;
25             }
26         }
27     }
28     if(t==-1)for(int i=0;i<n;i++)a[i].r/=n;
29 }
30
31 int a[N], dp[N], n;
32 void cdq(int l,int r){
33     if(l == r){
34         dp[l] = (dp[l]+a[l])%mod;
35         return;
36     }
37     int mid = l+r >> 1;
38     cdq(l, mid);
39     int len = 1;
40     while(len <= (r-l+1)) len <<= 1;
41     for(int i = 0; i < len; i++)
42         x[i] = y[i] = comp(0, 0);
43     for(int i = l; i <= mid; i++)
44         x[i-l] = comp(dp[i], 0);
45     for(int i = 1; l+i <= r; i++)
46         y[i-1] = comp(a[i], 0);
47     FFT(x, len, 1); FFT(y, len, 1);
48     for(int i = 0; i < len; i++)
49         x[i] = x[i]*y[i];
50     FFT(x, len, -1);
51
52     for(int i = mid+1; i <= r; i++){
53         dp[i] += x[i-l-1].r+0.5;
54         dp[i] %= mod;
55     }
56     cdq(mid+1,r);
57 }
58
59 int main(){
60     while(scanf("%d", &n), n){
61         for(int i = 1; i <= n; i++){
62             scanf("%d", a+i);
63             a[i] %= mod;
64             dp[i] = 0;
65         }
66         cdq(1, n);
67         printf("%d\n", dp[n]);
68     }
69     return 0;
70 }

补:

因为做多项式逆运算在分治FFT之前,故做此题时首先有了一个多项式求逆的方法。

观察 dp[n] = ∑ ( dp[n-i]*a[i] )+a[n], ( 1 <= i < n)

令a[0] = -1, 则 ∑ ( dp[n-i]*a[i] )+a[n] = 0, ( 0 <= i < n) 对任意n > 0成立

令dp[0] = 1

则dp序列与a序列卷积 = dp[0]*a[0]  +  ∑ ( dp[i-j]*a[j] ) xi = dp[0]*a[0] = -1,

已知a序列,则dp序列为a的逆多项式序列*(-1)。

因为模313,最终各个系数 <= n(m-1)*(m-1) = 9734400000,  故选了一个大素数206158430209

写了一个多项式求逆的代码,不过不知什么原因WA了。

 1 #include<cstdio>
 2 #include<iostream>
 3 typedef long long ll;
 4 using namespace std;
 5 const int N = 262144, K = 17;
 6 ll n, m, i, k;
 7 ll a[N+10], b[N+10], tmp[N+10], tmp2[N+10];
 8 ll P = 206158430209LL, G = 22, g[K+1], ng[K+10], inv[N+10], inv2;
 9 //ll P = 998244353, G = 3, g[K+1], ng[K+10], inv[N+10], inv2;
10 ////////////
11 ll mul(ll a, ll b){
12     ll ans = 0;
13     while(b){
14         if(b&1) ans += a;
15         if(ans >= P) ans -= P;
16         a = a+a;
17         if(a >= P) a -= P;
18         b >>= 1;
19     }
20     return ans;
21 }
22 ll pow(ll a, ll n){
23     ll ans = 1;
24     while(n){
25         if(n&1)
26             ans = mul(ans, a);
27         a = mul(a, a);
28         n >>= 1;
29     }
30     return ans;
31 }
32 ////////////
33 void NTT(ll*a,int n,int t){
34     for(int i=1,j=0;i<n-1;i++){
35         for(int s=n;j^=s>>=1,~j&s;);
36         if(i<j)swap(a[i], a[j]);
37     }
38     for(int d=0;(1<<d)<n;d++){
39         int m=1<<d,m2=m<<1;
40         ll _w=t==1?g[d]:ng[d];///////////////////////////////////
41
42         for(int i=0;i<n;i+=m2)for(ll w=1,j=0;j<m;j++){
43             ll&A=a[i+j+m],&B=a[i+j],t=mul(w, A);////////////(ll)w*A%P;
44             A=B-t;if(A<0)A+=P;
45             B=B+t;if(B>=P)B-=P;
46             w=mul(w, _w);/////////(ll)w*_w%P;/////////////////////
47         }
48     }
49     //if(t==-1)for(int i=0,j=inv[n];i<n;i++)a[i]=mul(a[i], j);///////(ll)a[i]*j%P;
50     if(t==-1){
51         ll j = pow(n, P-2);
52         for(int i=0;i<n;i++)a[i]=mul(a[i], j);
53     }
54 }
55 //给定a,求a的逆元b
56 void getinv(ll*a,ll*b,int n){
57     if(n==1){b[0]=pow(a[0],P-2);return;}
58     getinv(a,b,n>>1);
59     int k=n<<1,i;
60     for(i=0;i<n;i++)tmp[i]=a[i];
61     for(i=n;i<k;i++)tmp[i]=b[i]=0;
62     NTT(tmp,k,1),NTT(b,k,1);
63
64     for(i=0;i<k;i++){
65         ll xx = b[i];
66         b[i] = mul(xx, (P+2-mul(xx, tmp[i]))%P);
67     //b[i]=(ll)b[i]*(2-(ll)tmp[i]*b[i]%P)%P;
68     //if(b[i]<0)b[i]+=P;
69     }
70     NTT(b,k,-1);
71     for(i=n;i<k;i++)b[i]=0;
72 }
73 int main(){
74     for(g[K]=pow(G,(P-1)/N),ng[K]=pow(g[K],P-2),i=K-1;~i;i--){
75         //g[i]=(ll)g[i+1]*g[i+1]%P,ng[i]=(ll)ng[i+1]*ng[i+1]%P;
76         g[i] = mul(g[i+1], g[i+1]);
77         ng[i] = mul(ng[i+1], ng[i+1]);
78     }
79     //for(inv[1]=1,i=2;i<=N;i++)inv[i]=(ll)(P-inv[P%i])*(P/i)%P;inv2=inv[2];
80     while(scanf("%lld", &n), n){
81         int len = 1;
82         while(len <= n) len <<= 1;
83         //len <= 131072
84         a[0] = (P-1)%313;
85         for(i = 1; i <= n; i++){
86             scanf("%lld", a+i);
87             a[i] %= 313;
88         }
89         for(i = n+1; i < len; i++) a[i] = 0;
90
91         getinv(a, b, len);
92         b[n] = b[n]%313*(P-1LL)%313;
93 //        for(i = 0; i < len; i++)
94 //            b[i] = mul(b[i], P-1);
95 //              b[i] = b[i]*(P-1LL)%P;
96         printf("%lld\n", b[n]);
97     }
98     return 0;
99 }

时间: 2024-12-29 11:48:39

HDU5730 FFT+CDQ分治的相关文章

[玲珑OJ1044] Quailty and Binary Operation (FFT+cdq分治)

题目链接 题意:给定两个长度为n的数组a与长度为m的数组b, 给定一个操作符op满足 x op y = x < y ? x+y : x-y.  有q个询问,每次给出询问c,问:有多少对(i, j)满足a[i] op b[j] = c ? 0 <= c <= 100000, 其余数据范围在[0, 50000]. 题解:问题的关键在于如何分隔开 x < y与x >= y. cdq分治,合并的时候a[l, mid]与b[mid+1, r]卷积一次计算a[] < b[] , a

hdu 5730 Shell Necklace fft+cdq分治

题目链接 dp[n] = sigma(a[i]*dp[n-i]), 给出a1.....an, 求dp[n]. n为1e5. 这个式子的形式显然是一个卷积, 所以可以用fft来优化一下, 但是这样也是会超时的. 所以可以用cdq分治来优化. cdq分治就是处理(l, mid)的时候, 将dp[l]...dp[mid]对dp[mid+1]...dp[r]做的贡献都算出来. #include <bits/stdc++.h> using namespace std; #define pb(x) pus

HDU 5730 Shell Necklace(CDQ分治+FFT)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5730 [题目大意] 给出一个数组w,表示不同长度的字段的权值,比如w[3]=5表示如果字段长度为3,则其权值为5,现在有长度为n的字段,求通过不同拆分得到的字段权值乘积和. [题解] 记DP[i]表示长度为i时候的答案,DP[i]=sum_{j=0}^{i-1}DP[j]w[i-j],发现是一个卷积的式子,因此运算过程可以用FFT优化,但是由于在计算过程中DP[j]是未知值,顺次计算复杂度是O(

Shell Necklace (dp递推改cdq分治 + fft)

首先读出题意,然后发现这是一道DP,我们可以获得递推式为 然后就知道,不行啊,时间复杂度为O(n2),然后又可以根据递推式看出这里面可以拆解成多项式乘法,但是即使用了fft,我们还需要做n次多项式乘法,时间复杂度又变成O(n2 * log n),显然不可以.然后又利用c分治思维吧问题进行拆分问题但是,前面求出来的结果对后面的结果会产生影响,所以我们使用cdq分治思想来解决这个问题,时间复杂度变为O(n * log2n). #include<bits/stdc++.h> using namesp

HDU5322 Hope(DP + CDQ分治 + NTT)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5322 Description Hope is a good thing, which can help you conquer obstacles in your life, just keep fighting, and solve the problem below. In mathematics, the notion of permutation relates to the ac

【BZOJ3456】【CDQ分治+FNT】城市规划

试题来源 2013中国国家集训队第二次作业 问题描述 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案. 好了, 这就是困扰阿狸的问题.

BZOJ4555求和(cdq分治+NTT)

题意: 输出f(n)对998244353(7 × 17 × 223 + 1)取模的结果.1 ≤ n ≤ 100000 其中S(i,j)是第二类Stirling数,即有i个球,丢到j个盒子中,要求盒子不为空的方案总数 S(i,j)=S(i-1,j-1)+j*S(i-1,j) (前面一项表示第i个球单独放到一个盒子中,后面一项表示放到前面j个盒子中的某一个) 分析: 首先这个n不是丧心病狂的大,所以感觉可以求i=1时的结果,i=2时的结果,i=3时的结果……,于是可以不看第一个Σ 我们考虑后面的这项

CDQ分治与整体二分小结

前言 这是一波强行总结. 下面是一波瞎比比. 这几天做了几道CDQ/整体二分,感觉自己做题速度好慢啊. 很多很显然的东西都看不出来 分治分不出来 打不出来 调不对 上午下午晚上的效率完全不一样啊. 完蛋.jpg 绝望.jpg. 关于CDQ分治 CDQ分治,求的是三维偏序问题都知道的. 求法呢,就是在分治外面先把一维变成有序 然后分治下去,左边(l,mid)关于右边(mid+1,r)就不存在某一维的逆序了,所以只有两维偏序了. 这个时候来一波"树状数组求逆序对"的操作搞一下二维偏序 就可

【BZOJ3963】[WF2011]MachineWorks cdq分治+斜率优化

[BZOJ3963][WF2011]MachineWorks Description 你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM)的经理,公司使用更加先进的机械设备生产先进的机器.原来的那一台生产机器已经坏了,所以你要去为公司买一台新的生产机器.你的任务是在转型期内尽可能得到更大的收益.在这段时间内,你要买卖机器,并且当机器被ACM公司拥有的时候,操控这些机器以获取利润.因为空间的限制,ACM公司在任何时候都只能最多拥有一台机器. 在转型期内,有若