给两个长度为 \(n\) 的序列 \(a\) 和 \(b\) ,求知足 \(a_i+a_j>b_i+b_j \ (i<j)\) 的数对个数。c++
首先注意到这个式子左右两边同时出现了 \(i,j\) ,也就是说式子两边不是互相独立的。spa
嗯,天然地想到了移项大法,式子能够化为 \(a_i-b_i>b_j-a_j \ (i<j)\) 。式子的一边为 \(i\) ,一边为 \(j\) ,就使得式子两边互相独立。code
咱们发现这个偏序问题简直不要太好作,有不少的方法能够解决这个偏序问题,这里介绍一种 动态开点权值线段树 的作法。ip
倒序扫描,从 \(n\) 扫到 $1$ 。get
时间复杂度 \(\mathcal{O(n \log n)}\) ,空间复杂度 \(O(n \log size)\) ,若是离散化一下能够作到 \(O(n \log n)\) 。io
#include<cstdio> #define RI register int using namespace std; namespace IO { static char buf[1<<20],*fs,*ft; inline char gc() { if(fs==ft) { ft=(fs=buf)+fread(buf,1,1<<20,stdin); if(fs==ft)return EOF; } return *fs++; } #define gc() getchar() inline int read() { int x=0,f=1;char s=gc(); while(s<'0'||s>'9'){if(s=='-')f=-f;s=gc();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();} return x*f; } }using IO::read; const int N=200100,MLOGN=10001000; int n; int a[N],b[N]; const int INF=2e9; int tot,root; struct SegmentTree{ int lc,rc; int cnt; }t[MLOGN]; int New() { tot++; t[tot].lc=t[tot].rc=t[tot].cnt=0; return tot; } void insert(int &p,int l,int r,int delta,int val) { if(!p) p=New(); t[p].cnt+=val; if(l==r)return; int mid=(long long)(l+r)>>1; if(delta<=mid) insert(t[p].lc,l,mid,delta,val); else insert(t[p].rc,mid+1,r,delta,val); } int ask(int p,int l,int r,int s,int e) { if(!p) return 0; if(s<=l&&r<=e) return t[p].cnt; int mid=(long long)(l+r)>>1; int val=0; if(s<=mid) val+=ask(t[p].lc,l,mid,s,e); if(mid<e) val+=ask(t[p].rc,mid+1,r,s,e); return val; } long long ans; int main() { n=read(); for(RI i=1;i<=n;i++) a[i]=read(); for(RI i=1;i<=n;i++) b[i]=read(); for(RI i=n;i>=1;i--) { ans+=ask(root,-INF,INF,-INF,a[i]-b[i]-1); insert(root,-INF,INF,b[i]-a[i],1); } printf("%lld\n",ans); return 0; }