一道学校里div2 的训练题
题目大意是给出n(<=4000)行四列的数字(<=2^28),我们需要从每列中选取一个数字使得四个数字之和恰好为0,问有多少种选取方案。
所有组合是4000的4次方直接爆掉了,但是可以二分。(O(N)=N^2logN)
左边2列(4000*4000种)中各取一个数字求和,右边同理,排序后一个从小到大,另一个从大到小找。开始错的地方是没有考虑类似 "1 1 1 1"与 "-1 -1 -1 " 这种应该加 3*4 而不是++,于是就用了结构体数组将相同的和存在一起并计数。
1 #include<bits/stdc++.h> 2 #define EPS 1e-9 3 using namespace std; 4 5 typedef long long ll; 6 const int INF=0x3f3f3f3f; 7 const int MAXN=16000000+6; 8 9 long ans; 10 long Bin[2][MAXN]; 11 int cnt[2]; 12 int tot[2]; 13 int raw[4][4003]; 14 int N; 15 16 struct Node{ 17 int dat,num; 18 }BN[2][MAXN]; 19 20 void getBin(int t){ 21 for(int i=0;i<N;++i){ 22 for(int j=0;j<N;++j){ 23 Bin[t][cnt[t]++]=raw[2*t][i]+raw[2*t+1][j]; 24 } 25 } 26 } 27 28 int main(){ 29 //freopen("data.out","w",stdout); 30 int Test; 31 scanf("%d",&Test); 32 for(int cntT=1;cntT<=Test;++cntT){ 33 ans=0;cnt[0]=cnt[1]=0; 34 tot[0]=tot[1]=0; 35 scanf("%d",&N); 36 for(int i=0;i<N;++i){ 37 for(int j=0;j<4;++j){ 38 scanf("%d",&raw[j][i]); 39 } 40 } 41 getBin(0); 42 getBin(1); 43 sort(Bin[0],Bin[0]+cnt[0]); 44 sort(Bin[1],Bin[1]+cnt[1]); 45 46 for(int i=0;i<2;++i){ 47 BN[i][tot[i]].dat=Bin[i][0]; 48 BN[i][tot[i]++].num=1; 49 for(int j=1;j<cnt[i];++j) { 50 if(Bin[i][j]==BN[i][tot[i]-1].dat) 51 BN[i][tot[i]-1].num+=1; 52 else{ 53 BN[i][tot[i]].dat=Bin[i][j]; 54 BN[i][tot[i]++].num=1; 55 } 56 } 57 } 58 59 for(int i=0,j=tot[1]-1;i<tot[0];++i){ 60 while(BN[0][i].dat+BN[1][j].dat>0&&j>=1) j--; 61 if(BN[0][i].dat+BN[1][j].dat>0) break; 62 63 if(BN[0][i].dat+BN[1][j].dat==0) ans+=BN[0][i].num*BN[1][j].num; 64 else if(BN[0][i].dat+BN[1][j].dat<0) continue; 65 } 66 printf("%ld ",ans); 67 if(cntT!=Test) puts(""); 68 } 69 return 0; 70 }
数据会爆而且又是求和的性质啥的就应该往二分想。