$2016$长城信息杯中国大学生程序设计竞赛中南邀请赛$H$题
排序,二分。
对$a$数组,$b$数组从小到大进行排序。
统计每一个$a[i]$作为较大值的时候与$b[i]$对答案的贡献。反过来再统计以$b[i]$为较大值时与$a[i]$对答案的贡献。
以前者举例说明:
观察这个:$⌊\sqrt {|a[i] - b[j]|}⌋ $,按照题目中给出的范围,这个东西最大只有$1000$。
也就是说,我们在计算一个$a[i]$与$b[j]$对答案的贡献时候,不用从$1$到$m$枚举$j$,因为肯定是一段一段相同的,所以分段计算即可。二分一下就可以分段计算了。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<iostream> using namespace std; typedef long long LL; const double pi=acos(-1.0),eps=1e-8; const int maxn=100010; int n,m; int a[maxn],b[maxn]; int main() { while(~scanf("%d%d",&n,&m)) { for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) scanf("%d",&b[i]); sort(a+1,a+1+n); sort(b+1,b+1+m); LL ans=0; for(int i=1;i<=n;i++) { int L=1,R=m,pos=-1; while(L<=R) { int mid=(L+R)/2; if(b[mid]<a[i]) L=mid+1,pos=mid; else R=mid-1; } if(pos==-1) continue; int now=1, p; while(now<=pos) { int num=(int)(eps+sqrt(1.0*(a[i]-b[now]))); L=now,R=pos; while(L<=R) { int mid=(L+R)/2; int tmp=(int)(eps+sqrt(1.0*(a[i]-b[mid]))); if(tmp<num) R=mid-1; else L=mid+1,p=mid; } ans=ans+(LL)(p-now+1)*(LL)num; now=p+1; } } for(int i=1;i<=m;i++) { int L=1,R=n,pos=-1; while(L<=R) { int mid=(L+R)/2; if(a[mid]<b[i]) L=mid+1,pos=mid; else R=mid-1; } if(pos==-1) continue; int now=1, p; while(now<=pos) { int num=(int)(eps+sqrt(1.0*(b[i]-a[now]))); L=now,R=pos; while(L<=R) { int mid=(L+R)/2; int tmp=(int)(eps+sqrt(1.0*(b[i]-a[mid]))); if(tmp<num) R=mid-1; else L=mid+1,p=mid; } ans=ans+(LL)(p-now+1)*(LL)num; now=p+1; } } cout<<ans<<endl; } return 0; }