二元运算 FFT+分治
題目:
4836: [Lydsy2017年4月月賽]二元運(yùn)算
Time Limit: 8 Sec Memory Limit: 128 MB
Submit: 486 Solved: 162
[Submit][Status][Discuss]
Description
定義二元運(yùn)算 opt 滿足
現(xiàn)在給定一個(gè)長(zhǎng)為 n 的數(shù)列 a 和一個(gè)長(zhǎng)為 m 的數(shù)列 b ,接下來(lái)有 q 次詢問(wèn)。每次詢問(wèn)給定一個(gè)數(shù)字 c
你需要求出有多少對(duì) (i, j) 使得 a_i opt b_j=c 。
Input
第一行是一個(gè)整數(shù) T (1≤T≤10) ,表示測(cè)試數(shù)據(jù)的組數(shù)。
對(duì)于每組測(cè)試數(shù)據(jù):
第一行是三個(gè)整數(shù) n,m,q (1≤n,m,q≤50000) 。
第二行是 n 個(gè)整數(shù),表示 a_1,a_2,?,a_n (0≤a_1,a_2,?,a_n≤50000) 。
第三行是 m 個(gè)整數(shù),表示 b_1,b_2,?,b_m (0≤b_1,b_2,?,b_m≤50000) 。
第四行是 q 個(gè)整數(shù),第 i 個(gè)整數(shù) c_i (0≤c_i≤100000) 表示第 i 次查詢的數(shù)。
Output
對(duì)于每次查詢,輸出一行,包含一個(gè)整數(shù),表示滿足條件的 (i, j) 對(duì)的個(gè)數(shù)。
題解:
要求aioptbj=ca_i opt b_j = cai?optbj?=c的c的個(gè)數(shù),倘若題干中沒(méi)有分段操作的限制條件,那么這個(gè)題是一個(gè)裸的FFT加速求卷積的題目,比如如果要求ai+bj=ca_i + b_j = cai?+bj?=c的個(gè)數(shù),那么只需要構(gòu)建一個(gè)A序列,A[i]A[i]A[i]表示的是a數(shù)列中,值為i的元素的個(gè)數(shù),B序列同理,直接求個(gè)卷積就出來(lái)了。如果要求ai?bj=ca_i-b_j=cai??bj?=c的個(gè)數(shù),那么可以將B序列倒置,然后求卷積得到C數(shù)組,此時(shí)C[i]C[i]C[i]所表示的就是ai?bj=i?50000a_i-b_j=i-50000ai??bj?=i?50000的對(duì)數(shù)。
但是現(xiàn)在opt是一個(gè)分段操作,與x和y的關(guān)系有關(guān),我們就不能直接求卷積了。讓我們回想逆序數(shù)是怎么處理的,逆序數(shù)要求的是滿足下標(biāo)i<ji<ji<j的時(shí)候,a[i]>a[j]a[i]>a[j]a[i]>a[j]的對(duì)數(shù)。而我們這個(gè)題目要求的是滿足下標(biāo)i<ji<ji<j的時(shí)候,i+j=ci+j=ci+j=c的對(duì)數(shù),以及滿足下標(biāo)i>=ji>=ji>=j的時(shí)候,i?j=ci-j=ci?j=c的對(duì)數(shù)。是不是非常的相似,因此我們可以采用與求逆序數(shù)相同的方法,也就是分治法來(lái)解決。
solve(L,R)solve(L,R)solve(L,R)表示的是把子問(wèn)題[L,R]完全解決。
分治的時(shí)候,我們先solve(L,mid);solve(L,mid);solve(L,mid);和solve(mid,R);solve(mid,R);solve(mid,R);
然后把區(qū)間[L,mid][L,mid][L,mid]對(duì)區(qū)間[mid,R][mid,R][mid,R]的影響考慮進(jìn)去。
也就是對(duì)i<ji<ji<j的情況A[L,mid)A[L,mid)A[L,mid)和B[mid+1,R)B[mid+1,R)B[mid+1,R)做卷積。
然后對(duì)i>ji>ji>j,把B[L,mid)B[L,mid)B[L,mid)反轉(zhuǎn),然后再對(duì)A[mid+1,R)A[mid+1,R)A[mid+1,R)和B[L,mid)B[L,mid)B[L,mid)做卷積。
其中i=ji = ji=j的情況特殊考慮。
代碼:
#include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; double pi = acos(-1.0); struct complex{double re,im;complex(double r = 0.0,double i = 0.0):re(r),im(i){};complex operator+(complex com){return complex(re+com.re,im+com.im);}complex operator-(complex com){return complex(re-com.re,im-com.im);}complex operator*(complex com){return complex(re*com.re-im*com.im,re*com.im+im*com.re);} }; complex wn,wntmp; void rader(complex arr[],int n){int num = n-1;for(int i = 0;i < n;++i){int tn = n>>1;while(num && num >= tn) num ^= tn,tn >>= 1;num |= tn;if(num > i) swap(arr[i],arr[num]);} } void FFT(complex cs[],int n,int f){rader(cs,n);for(int s = 1;s < n;s <<= 1){wn = complex(cos(f*2*pi/(s*2)),sin(f*2*pi/(s*2)));for(int offset = 0;offset < n;offset += s<<1){wntmp = complex(1.0,0.0);for(int i = 0;i < s;++i){complex u = cs[offset+i],v = cs[offset+i+s]*wntmp;cs[offset+i] = u + v;cs[offset+i+s] = u - v;wntmp = wntmp * wn;}}}if(f == -1)for(int i = 0;i < n;++i)cs[i].re /= n; } int n,m,q; const int maxn = 400010,mi = 1; int a[maxn],b[maxn]; long long ans[maxn]; complex csA[maxn],csB[maxn],csC[maxn];void solve(int L,int R){if(R - L <= mi){return ;}int mid = (L + R)/2;solve(L,mid);solve(mid,R);int len = 1;while(len < mid - L || len < R - mid) len <<= 1;len <<= 1;for(int i = 0;i < len;++i) csC[i] = csA[i] = csB[i] = complex(0,0);for(int i = L;i < mid;++i) csA[i-L] = complex(a[i],0);for(int i = mid;i < R;++i) csB[i-mid] = complex(b[i],0);FFT(csA,len,1);FFT(csB,len,1);for(int i = 0;i < len;++i) csC[i] = csA[i]*csB[i];FFT(csC,len,-1);for(int i = 0;i < len;++i){long long tmp = (long long)(csC[i].re+0.5);ans[i+L+mid] += tmp;//if(tmp)//printf("%d += %d\n",i+L+mid,tmp);}for(int i = 0;i < len;++i) csC[i] = csA[i] = csB[i] = complex(0,0);for(int i = mid;i < R;++i) csA[i-mid] = complex(a[i]);for(int i = L;i < mid;++i) csB[i-L] = complex(b[mid-1-(i-L)]);FFT(csA,len,1);FFT(csB,len,1);for(int i = 0;i < len;++i) csC[i] = csA[i]*csB[i];FFT(csC,len,-1);for(int i = 0;i < len;++i){long long tmp = (long long)(csC[i].re+0.5);ans[i+1] += tmp;} } int main(){int T;scanf("%d",&T);while(T--){memset(a,0,sizeof(a));memset(b,0,sizeof(b));memset(ans,0,sizeof(ans));scanf("%d%d%d",&n,&m,&q);for(int i = 0;i < n;++i){int tmp;scanf("%d",&tmp);a[tmp]++;}for(int i = 0;i < m;++i){int tmp;scanf("%d",&tmp);b[tmp]++;}solve(0,50001);//cout<<a[50000]<<' '<<b[50000]<<endl;for(int i = 0;i < 50001;++i){ans[0] += a[i]*b[i];} for(int i = 0;i < q;++i){int tmp;scanf("%d",&tmp);printf("%lld\n",ans[tmp]);}} return 0; }總結(jié)
以上是生活随笔為你收集整理的二元运算 FFT+分治的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 可持久化线段树小结
- 下一篇: UVA4671 K-neighbor s