[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 5

Sample Output

1
0
1
0
0
1
0
1
0
1

HINT

Source

[Submit][Status][Discuss]

挺有意思的一道题,应该不难想到是分治FFT,主要是CDQ分治函数内部要想得很清楚。

首先如果只有加法,就是裸卷积。

如果只有减法,那么有$c_k=sumlimits_{i=k}^na_ib_{i-k}$,把b翻转,最后将c前移n位即可$c_{n+k}=sumlimits_{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
",ans[x]);
68     }
69     return 0;
70 }
原文地址:https://www.cnblogs.com/HocRiser/p/8689935.html