[cdq分治习题练习]

bzoj1935
题意:给出n棵树的位置(xi,yi),有m次询问,每次询问一个以(Xi,Yi)为左下角,(Li,Ri)为右上角的矩形内树的个数

题解:显然的三维偏序问题,一维是询问的时间T,一维是横坐标,一维是纵坐标。把初始的n棵树的位置当作插入,把询问当成4个二维前缀和相加减,由于时间是按照输入的顺序,所以第一维不需要排序,直接cdq分治处理第二维,树状数组维护第三维即可

/**************************************************************
    Problem: 1935
    User: Altria_
    Language: C++
    Result: Accepted
    Time:7168 ms
    Memory:278892 kb
****************************************************************/
#include 
 
using namespace std;
typedef long long ll;
//#define debug(x) cout << #x << " is " << x << endl;
 
const int maxn = 5e5+1000;
const int maxnw = 1e7+1000;
 
struct Op{
    ll x;
    ll y;
    int type;
    int id;
}op[maxn*5],op2[maxn*5];
 
ll tot,ans[maxn],tim,sz[maxnw],fl[maxnw],maxx;
 
void update(int x){
    for(int i=x;i<=maxx;i+=(i&(-i))){
        if(fl[i]!=tim){
            fl[i]=tim;
            sz[i]=0;
        }
        sz[i]++;
    }
}
 
ll query(int x){
    ll ac=0;
    for(int i=x;i>0;i-=(i&(-i))){
        if(fl[i]==tim){
            ac+=sz[i];
        }
    }
    return ac;
}
 
void cdq(int l,int r){
    if(l>=r)return;
 
    int mid=(l+r)>>1;
    cdq(l,mid);
    cdq(mid+1,r);
 
    int L=l;
    int R=mid+1;
    int t1=l;
    tim++;
    while(L<=mid&&R<=r){
        if(op[L].x<=op[R].x){
            if(op[L].type==0){
                update(op[L].y);
            }
            op2[t1++]=op[L++];
        }
        else{
            if(op[R].type)ans[op[R].id]+=op[R].type*query(op[R].y);
            op2[t1++]=op[R++];
        }
    }
    while(L<=mid){
        op2[t1++]=op[L++];
    }
    while(R<=r){
        if(op[R].type)ans[op[R].id]+=op[R].type*query(op[R].y);
        op2[t1++]=op[R++];
    }
    for(int i=l;i<=r;i++){
        op[i]=op2[i];
    }
}
 
int main() {
    int n,m;
    scanf("%d%d",&n,&m);
 
    for(int i=1;i<=n;i++){
        ++tot;
        scanf("%lld%lld",&op[tot].x,&op[tot].y);
        op[tot].x=op[tot].x+4;
        op[tot].y=op[tot].y+4;
        maxx=max(op[tot].y,maxx);
        op[tot].type=0;
        op[tot].id=0;
    }
    for(int i=1;i<=m;i++){
        ll a,b,c,d;
        ans[i]=0;
        scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
        a+=4;
        b+=4;
        c+=4;
        d+=4;
        maxx=max(max(b,d),maxx);
        op[++tot].id=i;
        op[tot].type=-1;
        op[tot].x=a-1;
        op[tot].y=d;
 
        op[++tot].id=i;
        op[tot].type=-1;
        op[tot].x=c;
        op[tot].y=b-1;
 
        op[++tot].id=i;
        op[tot].type=1;
        op[tot].x=a-1;
        op[tot].y=b-1;
 
        op[++tot].id=i;
        op[tot].type=1;
        op[tot].x=c;
        op[tot].y=d;
    }
 
    cdq(1,tot);
 
    for(int i=1;i<=m;i++){
        printf("%lld\n",ans[i]);
    }
    return 0;
}

bzoj1176
题意:有一个w*w的矩阵,每个格子内数字的初始值为s,有m次操作,操作有两种:1、格子(xi,yi)数字增加 2、求某个子矩阵格子数字之和

题解:和上题一样

/**************************************************************
    Problem: 1176
    User: Altria_
    Language: C++
    Result: Accepted
    Time:6184 ms
    Memory:46692 kb
****************************************************************/
 
#include 
 
using namespace std;
typedef long long ll;
#define debug(x) cout << #x << " is " << x << endl;
 
const int maxn = 2e5+1000;
const int maxnw = 2e6+1000;
 
struct Op{
    ll x;
    ll y;
    int type;
    ll val;
    int id;
}op[maxn],op2[maxn];
 
ll ans[maxn],sum[maxnw],fl[maxnw],maxx,tim;
 
void update(int x,ll d){
    while(x<=maxx){
        if(fl[x]!=tim)fl[x]=tim,sum[x]=0;
        sum[x]+=d;
        x+=(x&(-x));
    }
}
 
ll query(int x){
    ll ac=0;
    while(x>0){
        if(fl[x]==tim)ac+=sum[x];
        x-=(x&(-x));
 
    }
    return ac;
}
 
void cdq(int l,int r){
    if(l>=r)return;
    int mid=(l+r)>>1;
    cdq(l,mid);
    cdq(mid+1,r);
    int t1=l-1;
    int L=l;
    int R=mid+1;
    tim++;
    while(L<=mid&&R<=r){
        if(op[L].x<=op[R].x){
            if(op[L].type==0){
                update(op[L].y,op[L].val);
            }
            op2[++t1]=op[L++];
        }
        else{
            if(op[R].type){
                ans[op[R].id]+=op[R].type*query(op[R].y);
            }
            op2[++t1]=op[R++];
        }
    }
    while(L<=mid){
        op2[++t1]=op[L++];
    }
    while(R<=r){
        if(op[R].type){
            ans[op[R].id]+=op[R].type*query(op[R].y);
        }
        op2[++t1]=op[R++];
    }
    for(int i=l;i<=r;i++){
        op[i]=op2[i];
    }
}
 
int main() {
    ll s,n;
    scanf("%lld%lld",&s,&n);
    int a;
    int tot=0;
    scanf("%d",&a);
    int ak=0;
    maxx=2e6;
    while(1){
        if(a==3)break;
        else if(a==1){
            int x,y;
            ll b;
            scanf("%d%d%lld",&x,&y,&b);
            tot++;
            op[tot].id=0;
            op[tot].type=0;
            op[tot].x=x;
            op[tot].y=y;
            op[tot].val=b;
        }
        else{
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            tot++;
            ak++;
            op[tot].id=ak;
            op[tot].type=1;
            op[tot].x=x1-1;
            op[tot].y=y1-1;
            tot++;
            op[tot].id=ak;
            op[tot].type=-1;
            op[tot].x=x2;
            op[tot].y=y1-1;
            tot++;
            op[tot].id=ak;
            op[tot].type=-1;
            op[tot].x=x1-1;
            op[tot].y=y2;
            tot++;
            op[tot].id=ak;
            op[tot].type=1;
            op[tot].x=x2;
            op[tot].y=y2;
        }
        scanf("%d",&a);
    }
    cdq(1,tot);
    for(int i=1;i<=ak;i++){
        printf("%lld\n",ans[i]+s);
    }
    return 0;
}

bzoj3295
题意:给出一个序列,有m次删除操作,求出每次删除操作发生前序列的逆序对个数

题解:容易想到倒序做这道题,删除改为插入。

但是这道题不同于之前两道题,因为每次插入操作对逆序对个数产生的贡献不光受到左边元素的影响还会收到右边元素的影响,之前的题目都是元素只受到位置在它左边元素影响所以可以很容易对其中一维进行cdq分治。

考虑cdq分治只能计算左边元素对右边元素的贡献,再考虑某个元素,原序列中位置在它左边的元素因为本身就在它左边了,所以很容易按照位置(从小到大的顺序)这一维用cdq分治处理,用树状数组计算值上面的贡献,同理要想计算原序列位置在它右边的元素对它的影响,那么就要按照某一维排序,显然右边元素的值比它小的可以产生贡献,所以可以按照值(从小到大的顺序)这一维用cdq分治处理,用树状数组计算位置上面的贡献

/**************************************************************
    Problem: 3295
    User: Altria_
    Language: C++
    Result: Accepted
    Time:3544 ms
    Memory:11772 kb
****************************************************************/

#include

using namespace std;
typedef long long ll;
//#define debug(x) cout<<#x<<" is "<

const int maxn=1e5+5;

struct Pot{
    int pos;
    int val;
    int tim;
    int id;
    int type;
}p[maxn],p2[maxn],p3[maxn];

ll ans[maxn],sum[maxn],tim,f[maxn];

bool cmp(struct Pot a,struct Pot b){
    return a.tim<b.tim;
}

int lowbit(int x){
    return x&(-x);
}

void update(int x){
    for(int i=x;i>0;i-=lowbit(i)){
        if(f[i]!=tim){
            f[i]=tim;
            sum[i]=0;
        }
        sum[i]++;
    }
}

ll query(int x){
    ll ac=0;
    for(int i=x;i<maxn;i+=lowbit(i)){
        if(f[i]==tim)ac+=sum[i];
    }
    return ac;
}

void cdq1(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    cdq1(l,mid);
    cdq1(mid+1,r);
    int L=l;
    int R=mid+1;
    tim++;
    int t=l-1;
    while(L<=mid&&R<=r){
        if(p[L].pos<p[R].pos){
            update(p[L].val);
            p2[++t]=p[L++];
        }
        else{
            ans[p[R].id]+=query(p[R].val);
            p2[++t]=p[R++];
        }
    }
    while(L<=mid)p2[++t]=p[L++];
    while(R<=r){
        ans[p[R].id]+=query(p[R].val);
        p2[++t]=p[R++];
    }
    for(int i=l;i<=r;i++)p[i]=p2[i];
}

void cdq2(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    cdq2(l,mid);
    cdq2(mid+1,r);
    int L=l;
    int R=mid+1;
    tim++;
    int t=l-1;
    while(L<=mid&&R<=r){
        if(p3[L].val<p3[R].val){
            update(p3[L].pos);
            p2[++t]=p3[L++];
        }
        else{
            ans[p3[R].id]+=query(p3[R].pos);
            p2[++t]=p3[R++];
        }
    }
    while(L<=mid)p2[++t]=p3[L++];
    while(R<=r){
        ans[p3[R].id]+=query(p3[R].pos);
        p2[++t]=p3[R++];
    }
    for(int i=l;i<=r;i++)p3[i]=p2[i];
}

void print(ll sum,int pos,int en){
    if(pos==en)return;
    print(sum+ans[pos+1],pos+1,en);
    printf("%lld\n",sum+ans[pos+1]);
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);

    for(int i=1;i<=n;i++){
        int a;
        scanf("%d",&a);
        p[a].val=a;
        p[a].pos=i;
        p[a].tim=i;
        p[a].type=0;
        p[a].id=0;
    }

    for(int i=1;i<=m;i++){
        int b;
        scanf("%d",&b);
        p[b].tim=m-i+1+2*n;
        p[b].id=m-i+1;
    }

    sort(p+1,p+1+n,cmp);

    for(int i=1;i<=n;i++)p3[i]=p[i];

    cdq2(1,n);

    print(ans[0],0,m);

    return 0;
}

洛谷P4169
题意:给出n个玩偶的位置(xi,yi),m次询问,每次给出一个新木偶的位置或者询问距离某个位置(Xi,Yi)最近的木偶的距离(其中距离是指|Xi-xi|+|Yi-yi|)

题解:转化成4种情况,在(Xi,Yi)左下角、右下角、左上角、右上角的木偶的最近距离,例如在左下角时|Xi-xi|+|Yi-yi|=(Xi+Yi)-(xi+yi),所以可以cdq分治处理横坐标(或纵坐标),树状数组维护纵坐标(或者横坐标)区间内(xi+yi)的最值。则4个独立的cdq分治即可解决

// luogu-judger-enable-o2
#include

using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" is "<

const int maxn=6e5+5;
const int maxnv=1e6+5;

struct Pot{
    int x;
    int y;
    int type;
    int id;
}p[maxn],p2[maxn],p3[maxn],p4[maxn],p5[maxn];

int ans[maxn],maxx[maxnv],mw;
ll f[maxnv],tim;
const int inf=1e8;

int lowbit(int x){
    return x&(-x);
}

void update(int x,int y){
    for(register int i=x;i<mw;i+=lowbit(i)){
        if(f[i]!=tim){maxx[i]=-inf,f[i]=tim;}
        maxx[i]=max(maxx[i],y);
    }
}

void update2(int x,int y){
    for(register int i=x;i>0;i-=lowbit(i)){
        if(f[i]!=tim)f[i]=tim,maxx[i]=-inf;
        maxx[i]=max(maxx[i],y);
    }
}

int query(int x){
    int s=-inf;
    for(register int i=x;i>0;i-=lowbit(i)){
        if(f[i]==tim)s=max(s,maxx[i]);
    }
    return s;
}

int query2(int x){
    int s=-inf;
    for(register int i=x;i<mw;i+=lowbit(i)){
        if(f[i]==tim)s=max(s,maxx[i]);
    }
    return s;
}

void cdq(int l,int r){
    if(l==r)return;

    int mid=(l+r)>>1;
    cdq(l,mid);
    cdq(mid+1,r);

    int L=l;
    int R=mid+1;
    int t=l-1;
    tim++;
    while(L<=mid&&R<=r){
        if(p[L].x<=p[R].x){
            if(!p[L].type)update(p[L].y,p[L].y+p[L].x);
            p5[++t]=p[L++];
        }
        else{
            if(p[R].type)ans[p[R].id]=min(ans[p[R].id],p[R].x+p[R].y-query(p[R].y));
            p5[++t]=p[R++];
        }
    }
    while(L<=mid)p5[++t]=p[L++];
    while(R<=r){
        if(p[R].type)ans[p[R].id]=min(ans[p[R].id],p[R].x+p[R].y-query(p[R].y));
        p5[++t]=p[R++];
    }
    for(register int i=l;i<=r;i++){
        p[i]=p5[i];
    }
}

void cdq2(int l,int r){
    if(l==r)return;

    int mid=(l+r)>>1;
    cdq2(l,mid);
    cdq2(mid+1,r);

    int L=l;
    int R=mid+1;
    int t=l-1;
    tim++;
    while(L<=mid&&R<=r){
        if(p2[L].x<=p2[R].x){
            if(!p2[L].type)update2(p2[L].y,p2[L].x-p2[L].y);
            p5[++t]=p2[L++];
        }
        else{
            if(p2[R].type)ans[p2[R].id]=min(ans[p2[R].id],p2[R].x-p2[R].y-query2(p2[R].y));
            p5[++t]=p2[R++];
        }
    }
    while(L<=mid)p5[++t]=p2[L++];
    while(R<=r){
        if(p2[R].type)ans[p2[R].id]=min(ans[p2[R].id],p2[R].x-p2[R].y-query2(p2[R].y));
        p5[++t]=p2[R++];
    }
    for(register int i=l;i<=r;i++){
        p2[i]=p5[i];
    }
}

void cdq3(int l,int r){
    if(l==r)return;

    int mid=(l+r)>>1;
    cdq3(l,mid);
    cdq3(mid+1,r);

    int L=l;
    int R=mid+1;
    int t=l-1;
    tim++;
    while(L<=mid&&R<=r){
        if(p3[L].x>=p3[R].x){
            if(!p3[L].type)update(p3[L].y,-p3[L].x+p3[L].y);
            p5[++t]=p3[L++];
        }
        else{
            if(p3[R].type)ans[p3[R].id]=min(ans[p3[R].id],-p3[R].x+p3[R].y-query(p3[R].y));
            p5[++t]=p3[R++];
        }
    }
    while(L<=mid)p5[++t]=p3[L++];
    while(R<=r){
        if(p3[R].type)ans[p3[R].id]=min(ans[p3[R].id],-p3[R].x+p3[R].y-query(p3[R].y));
        p5[++t]=p3[R++];
    }
    for(register int i=l;i<=r;i++){
        p3[i]=p5[i];
    }
}

void cdq4(int l,int r){
    if(l==r)return;

    int mid=(l+r)>>1;
    cdq4(l,mid);
    cdq4(mid+1,r);

    int L=l;
    int R=mid+1;
    int t=l-1;
    tim++;
    while(L<=mid&&R<=r){
        if(p4[L].x>=p4[R].x){
            if(!p4[L].type)update2(p4[L].y,-p4[L].x-p4[L].y);
            p5[++t]=p4[L++];
        }
        else{
            if(p4[R].type)ans[p4[R].id]=min(ans[p4[R].id],-p4[R].x-p4[R].y-query2(p4[R].y));
            p5[++t]=p4[R++];
        }
    }
    while(L<=mid)p5[++t]=p4[L++];
    while(R<=r){
        if(p4[R].type)ans[p4[R].id]=min(ans[p4[R].id],-p4[R].x-p4[R].y-query2(p4[R].y));
        p5[++t]=p4[R++];
    }
    for(register int i=l;i<=r;i++){
        p4[i]=p5[i];
    }
}

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch>'9' || ch<'0') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0' && ch<='9') { x=x*10+ch-'0'; ch=getchar(); }
    return x*f;
}

int main(){
    int n,m;
    //scanf("%d%d",&n,&m);
    n=read();
    m=read();

    for(int i=1;i<=n;i++){
        //scanf("%d%d",&p[i].x,&p[i].y);
        p[i].x=read();
        p[i].y=read();
        p[i].y++;
        mw=max(mw,p[i].y);
        p[i].id=0;
        p[i].type=0;
    }

    int tot=0;
    for(int i=1;i<=m;i++){
        int t=read();
        p[i+n].x=read();
        p[i+n].y=read();
        p[i+n].y++;
        mw=max(mw,p[i+n].y);

        if(t==1){
            p[i+n].type=0;
            p[i+n].id=0;
        }
        else{
            p[i+n].type=1;
            p[i+n].id=++tot;
        }
    }

    for(register int i=1;i<=n+m;i++){
        p4[i]=p3[i]=p2[i]=p[i];
    }
    memset(ans,0x3f3f3f3f,sizeof(ans));

    tim=0;
    mw++;
    cdq(1,n+m);

    cdq2(1,n+m);

    cdq3(1,n+m);

    cdq4(1,n+m);

    for(register int i=1;i<=tot;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}

你可能感兴趣的:(cdq分治)