打开CDQ的大门&BZOJ3262

题目传送门

第一次接触CDQ分治,感谢YZ大佬的教导。

CDQ分治就是一种奇特的分治方法,它用左区间的区间信息来更新右区间。

设CDQ(L,R,l,r)表示递归到区间[L,R],区间的值为[l,r]。

mid=l+r/2。

将L~R区间按<=mid和>mid的两块重新分开。

继续递归区间分治。

CDQ分治用于解决一类偏序问题,题目中所求的为三维偏序。

即计算一类ai<bi,aj<bj,ak<bk的贡献。

普通的二维偏序大家都做过,经典题目逆序对。

三维怎么做呢?

第一维依然是SORT,第三位是树状数组。

第二维是CDQ。

第一维SORT后有序,故无需考虑第一维。

第二维CDQ分治时枚举L~R区间,当a[i].y>mid时,计算在权值树状数组上的贡献,否则插入树状数组。

注意:这道题需要对重复区间判重,否则贡献值不好算。

code:

/**************************************************************
    Problem: 3262
    User: yekehe
    Language: C++
    Result: Accepted
    Time:1116 ms
    Memory:10600 kb
****************************************************************/
 
#include <cstdio>
#include <algorithm>
using namespace std;
 
char tc()
{
    static char tr[10000],*A=tr,*B=tr;
    return A==B&&(B=(A=tr)+fread(tr,1,10000,stdin),A==B)?EOF:*A++;
}
 
int read()
{
    char c;while(c=tc(),c<'0'||c>'9');
    int x=c-'0';while(c=tc(),c>='0'&&c<='9')x=x*10+c-'0';
    return x;
}
 
struct node{
    int x,y,z,o;
}a[100005],lx[100005],rx[100005],Ne[100005];
int N,K,cnt;
int at[200005],b[200005],tim,ans[200005],tot[200005];
inline int cmp(node x,node y){return x.x<y.x || x.x==y.x&&x.y<y.y || x.x==y.x&&x.y==y.y&&x.z<y.z;}
 
void add(int x,int y)
{
    for(int i=x;i<=K;i+=i&-i){
        if(b[i]!=tim)at[i]=0,b[i]=tim;
        at[i]+=y;
    }
    return ;
}
 
int get(int x)
{
    int tot=0;
    for(int i=x;i;i-=i&-i)
        if(!(b[i]^tim))tot+=at[i];
    return tot;
}
 
void cdq(int L,int R,int l,int r)
{
    tim++;
    int mid=l+r>>1,lt=0,rt=0;
        for(int i=L;i<=R;i++){
            if(!(l^r)){
                ans[Ne[i].o]+=get(Ne[i].z);
                add(Ne[i].z,tot[Ne[i].o]);
            }
            else{
                if(Ne[i].y<=mid)add(Ne[i].z,tot[Ne[i].o]);
                else ans[Ne[i].o]+=get(Ne[i].z);
            }
        }//统计贡献
        for(int i=L;i<=R;i++){
            if(Ne[i].y<=mid)lx[++lt]=Ne[i];
            else rx[++rt]=Ne[i];
        }
        for(int i=1;i<=lt;i++)Ne[L+i-1]=lx[i];
        for(int i=1;i<=rt;i++)Ne[L+i-1+lt]=rx[i];//类似于归并那样重组
    if(!(l^r))return ;
    cdq(L,L+lt-1,l,mid);
    cdq(L+lt,R,mid+1,r);
    return ;
}
 
int IS(int x,int y){
    return a[x].x==a[y].x&&a[x].y==a[y].y&&a[x].z==a[y].z;
}
 
int res[100005];
 
int main()
{
    N=read(),K=read();
        for(int i=1;i<=N;i++){
            a[i].x=read(),a[i].y=read(),a[i].z=read();
        }
    sort(a+1,a+N+1,cmp);
    a[0].x=a[0].y=a[0].z=-2e9;
        for(int i=1;i<=N;i++){
            if(IS(i,i-1))++tot[cnt];
            else tot[++cnt]=1,Ne[cnt]=a[i],Ne[cnt].o=cnt;
        }//去重
    cdq(1,cnt,0,K);//注意为cnt
        for(int i=1;i<=cnt;i++)res[ans[i]+tot[i]-1]+=tot[i];
        for(int i=0;i<N;i++)printf("%d\n",res[i]);
    return 0;
}

 

posted @ 2018-04-03 16:07  Cptraser  阅读(190)  评论(0编辑  收藏  举报