P3157 [CQOI2011]动态逆序对 CDQ分治
一道CDQ分治模板题
简单来说,这道题是三维数点
对于离线的二维数点,我们再熟悉不过:利用坐标的单调递增性,先按更坐标排序,再按纵坐标排序
更新和查询时都直接调用纵坐标。
实际上,我们是通过排序将二维中的一维给消掉了。
那么,对于题中的三维数点,我们可以先按 $x$ 排序,以 $x$ 为标准进行分治
在分治的过程中,分别对左右区间按 $y$ 来排序。
由于左面的 $x$ 坐标一定是小于右面的 $x$ 坐标的。
所以这一维可以被我们消掉。
剩下的就是一个二维数点问题了,只需按照顺序依次更新 $z$ 即可。
(反正左面的 $x$ 比右面小,而且 $y$ 也一定比右面小,只要 $z$ 比右面小是都能统计出来的)
Code:
#include <cstdio> #include <algorithm> #include <cstring> #define setIO(s) freopen(s".in","r",stdin) ,freopen(s".out","w",stdout) #define maxn 1000006 using namespace std; int n,m,idx,place[maxn],C[maxn]; long long ans[maxn]; struct BIT{ int lowbit(int x) { return x&(-x); } void add(int pos,int x){ while(pos<=n) C[pos]+=x,pos+=lowbit(pos); } int ask(int pos){ int sum=0; while(pos>0) sum+=C[pos],pos-=lowbit(pos); return sum; } }tree; struct OPT{ int x,y,z,rnk,kind,id; }opt[maxn],tmp[maxn]; bool cmp(OPT i,OPT j) { return (i.x==j.x&&i.y==j.y)?(i.rnk<j.rnk):((i.x==j.x)?i.y<j.y:i.x<j.x); } void solve(int L,int R) { if(L>=R) return; int mid=(L+R)>>1,tl=L-1,tr=mid; for(int i=L;i<=R;++i){ if(opt[i].rnk<=mid&&opt[i].kind==1) tree.add(opt[i].y,opt[i].z); if(opt[i].rnk>mid&&opt[i].kind==2) ans[opt[i].id]+=tree.ask(opt[i].y)*opt[i].z; } for(int i=L;i<=R;++i) if(opt[i].rnk<=mid&&opt[i].kind==1) tree.add(opt[i].y,-opt[i].z); for(int i=L;i<=R;++i) { if(opt[i].rnk<=mid) tmp[++tl]=opt[i]; else tmp[++tr]=opt[i]; } for(int i=L;i<=R;++i) opt[i]=tmp[i]; solve(L,mid),solve(mid+1,R); } int main(){ //setIO("input"); scanf("%d%d",&n,&m); for(int i=1,a;i<=n;++i) { scanf("%d",&a); opt[++idx].x=i; opt[idx].y=a; opt[idx].z=1; opt[idx].kind=1; opt[idx].rnk=idx; place[a]=i; tree.add(a,1); ans[1]+=i-tree.ask(a); } for(int i=1;i<=n;++i) tree.add(i,-1); for(int i=1,a;i<=m;++i){ scanf("%d",&a); opt[++idx].x=place[a]; opt[idx].y=n; opt[idx].z=-1; opt[idx].kind=2; opt[idx].rnk=idx; opt[idx].id=i+1; opt[++idx].x=n; opt[idx].y=a; opt[idx].z=-1; opt[idx].kind=2; opt[idx].rnk=idx; opt[idx].id=i+1; opt[++idx].x=place[a]; opt[idx].y=a; opt[idx].z=2; opt[idx].kind=2; opt[idx].rnk=idx; opt[idx].id=i+1; opt[++idx].x=place[a]; opt[idx].y=a; opt[idx].z=-1; opt[idx].kind=1; opt[idx].rnk=idx; } sort(opt+1,opt+1+idx,cmp); solve(1,idx); for(int i=2;i<=m;++i) ans[i]+=ans[i-1]; for(int i=1;i<=m;++i) printf("%lld\n",ans[i]); return 0; }