[BZOJ4836]二元运算(分治FFT)
4836: [Lydsy1704月赛]二元运算
Time Limit: 8 Sec Memory Limit: 128 MB
Submit: 578 Solved: 202
[Submit][Status][Discuss]Description
定义二元运算 opt 满足现在给定一个长为 n 的数列 a 和一个长为 m 的数列 b ,接下来有 q 次询问。每次询问给定一个数字 c你需要求出有多少对 (i, j) 使得 a_i opt b_j=c 。Input
第一行是一个整数 T (1≤T≤10) ,表示测试数据的组数。对于每组测试数据:第一行是三个整数 n,m,q (1≤n,m,q≤50000) 。第二行是 n 个整数,表示 a_1,a_2,?,a_n (0≤a_1,a_2,?,a_n≤50000) 。第三行是 m 个整数,表示 b_1,b_2,?,b_m (0≤b_1,b_2,?,b_m≤50000) 。第四行是 q 个整数,第 i 个整数 c_i (0≤c_i≤100000) 表示第 i 次查询的数。Output
对于每次查询,输出一行,包含一个整数,表示满足条件的 (i, j) 对的个数。
Sample Input
2
2 1 5
1 3
2
1 2 3 4 5
2 2 5
1 3
2 4
1 2 3 4 5Sample Output
1
0
1
0
0
1
0
1
0
1HINT
Source
[Submit][Status][Discuss]
挺有意思的一道题,应该不难想到是分治FFT,主要是CDQ分治函数内部要想得很清楚。
首先如果只有加法,就是裸卷积。
如果只有减法,那么有$c_k=\sum\limits_{i=k}^na_ib_{i-k}$,把b翻转,最后将c前移n位即可$c_{n+k}=\sum\limits_{i=0}^na_{k+i}b_{n-i}$
现在有了大小关系的限制,我们可以通过$CDQ$分治处理。
首先特判掉相等的情况,然后对于$[l,r]$这个区间,将$[l,mid]$和$[mid+1,r]$递归处理,现在问题只剩下$a[l,mid]$和$b[mid+1,r]$,$a[mid+1,r]$和$b[l,mid]$两个问题了。
显然前一个问题直接将$a$前移$l$位,$b$前移$mid+1$位,卷起来之后再后移$l+mid+1$位即可。后一个问题,将$a$前移$mid+1$位,b翻转后前移$l$位,这样卷之后的区间下标是从$0$开始的,而我们的差是从$1$开始的,所以右移$1$位。
卷的时候还是一定要注意次数界!NTT也可以。
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define mem(a) memset(a,0,sizeof(a)) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=500010; 10 const double pi=acos(-1.); 11 struct C{ double x,y; }A[N],B[N]; 12 int a[N],b[N],rev[N],n,T,x,m,Q,mx,len1,len2; 13 ll ans[N]; 14 15 C operator +(C &a,C &b){ return (C){a.x+b.x,a.y+b.y}; } 16 C operator -(C &a,C &b){ return (C){a.x-b.x,a.y-b.y}; } 17 C operator *(const C &a,const C &b){ return (C){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x}; } 18 19 void DFT(C a[],int n,int f){ 20 for (int i=0; i<n; i++) if (i<rev[i]) swap(a[i],a[rev[i]]); 21 for (int i=1; i<n; i<<=1){ 22 C wn=(C){cos(pi/i),f*sin(pi/i)}; 23 for (int p=i<<1,j=0; j<n; j+=p){ 24 C w=(C){1,0}; 25 for (int k=0; k<i; k++,w=w*wn){ 26 C x=a[j+k],y=w*a[i+j+k]; a[j+k]=x+y; a[i+j+k]=x-y; 27 } 28 } 29 } 30 if (f==1) return; 31 for (int i=0; i<n; i++) a[i].x/=n; 32 } 33 34 void CDQ(int l,int r){ 35 if (l==r) return; 36 int mid=(l+r)>>1,n,L=0; 37 for (n=1; n<=r-l+1; n<<=1) L++; 38 for (int i=0; i<n; i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1)); 39 40 for (int i=0; i<n; i++) A[i]=B[i]=(C){0,0}; 41 for (int i=l; i<=mid; i++) A[i-l].x=a[i]; 42 for (int i=mid+1; i<=r; i++) B[i-mid-1].x=b[i]; 43 DFT(A,n,1); DFT(B,n,1); 44 for (int i=0; i<n; i++) A[i]=A[i]*B[i]; 45 DFT(A,n,-1); 46 for (int i=0; i<n; i++) ans[i+mid+1+l]+=(ll)(A[i].x+0.5); 47 48 for (int i=0; i<n; i++) A[i]=B[i]=(C){0,0}; 49 for (int i=mid+1; i<=r; i++) A[i-mid-1].x=a[i]; 50 for (int i=l; i<=mid; i++) B[mid-i].x=b[i]; 51 DFT(A,n,1); DFT(B,n,1); 52 for (int i=0; i<n; i++) A[i]=A[i]*B[i]; 53 DFT(A,n,-1); 54 for (int i=0; i<n; i++) ans[i+1]+=(ll)(A[i].x+0.5); 55 CDQ(l,mid); CDQ(mid+1,r); 56 } 57 58 int main(){ 59 freopen("bzoj4836.in","r",stdin); 60 freopen("bzoj4836.out","w",stdout); 61 for (scanf("%d",&T); T--; ){ 62 scanf("%d%d%d",&n,&m,&Q); mem(ans); mem(a); mem(b); len1=len2=0; 63 for (int i=1; i<=n; i++) scanf("%d",&x),a[x]++,len1=max(len1,x); 64 for (int i=1; i<=m; i++) scanf("%d",&x),b[x]++,len2=max(len2,x); 65 for (int i=1; i<=len1 && i<=len2; i++) ans[0]+=1ll*a[i]*b[i]; 66 CDQ(0,max(len1,len2)); 67 for (int i=1; i<=Q; i++) scanf("%d",&x),printf("%lld\n",ans[x]); 68 } 69 return 0; 70 }