[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=\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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM