【CDQ分治/带修主席树】The 2019 Asia Nanchang First Round Online Programming Contest - I - Yukino With Subint

题目链接https://nanti.jisuanke.com/t/41356


题意

给出一个序列,有两个操作

  1. 修改一个数字
  2. 询问一段区间内数字在一定范围内的最长连续段有多少。

题解

按照题解的思路把原数组 a a a转变为 b b b,其中 b [ i ] = a [ i ] b[i]=a[i] b[i]=a[i]当且仅当 a [ i ] = ̸ a [ i − 1 ] a[i]=\not a[i-1] a[i]≠a[i1],否则 b [ i ] = 0 b[i]=0 b[i]=0
题目转变为对 b b b数组单点修改,区间查询在范围内的数字个数(加上左端点的特判)。
可以用带修主席树直接维护,也可以用三维CDQ,一维操作顺序,一维位置,一维权值。


解法1:CDQ
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N=2e6+7;
const int inf=1e8+7;
int n,m,tot;
struct Node{
    int o,pos,val,id;
}op1[N],op2[N];
int a[N],b[N];
int c[N];
int ans[N];
int lb(int x){return x&(-x);}
void add(int x,int v){
    if(x<=0) return;
    while(x<=n){
        c[x]+=v;
        x+=lb(x);
    }
}
int ask(int x){
    int res=0;
    while(x){
        res+=c[x];
        x-=lb(x);
    }
    return res;
}
void cdq(int l,int r){
    if(l==r) return;
    int m=l+r>>1;
    cdq(l,m);cdq(m+1,r);
    int p=l,q=m+1,i=l;
    while(p<=m&&q<=r){
        if(op1[p].pos<=op1[q].pos){
            if(op1[p].o==1) add(op1[p].val,1);
            else if(op1[p].o==2) add(op1[p].val,-1);
            op2[i++]=op1[p++];
        }
        else{
            if(op1[q].o==3) ans[op1[q].id]+=ask(op1[q].val);
            else if(op1[q].o==4) ans[op1[q].id]-=ask(op1[q].val);
            op2[i++]=op1[q++];
        }
    }
    while(p<=m){
        if(op1[p].o==1) add(op1[p].val,1);
        else if(op1[p].o==2) add(op1[p].val,-1);
        op2[i++]=op1[p++];
    }
    while(q<=r){
        if(op1[q].o==3) ans[op1[q].id]+=ask(op1[q].val);
        else if(op1[q].o==4) ans[op1[q].id]-=ask(op1[q].val);
        op2[i++]=op1[q++];
    }
    for(int i=l;i<=m;i++){
        if(op1[i].o==1) add(op1[i].val,-1);
        else if(op1[i].o==2) add(op1[i].val,1);
    }
    for(int i=l;i<=r;i++) op1[i]=op2[i];
}
int main()
{
    scanf("%d%d",&n,&m);
    int tot=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i]=a[i]==a[i-1]?0:a[i];
        if(b[i]) op1[++tot]=(Node){1,i,b[i],0};
    }
    for(int i=1,o;i<=m;i++){
        scanf("%d",&o);
        if(o==1){
            ans[i]=-1;
            int pos,v;
            scanf("%d%d",&pos,&v);
            if(a[pos]==v) continue;
            op1[++tot]=(Node){2,pos,b[pos],i};
            if(a[pos]==a[pos+1]){
                b[pos+1]=a[pos+1];
                op1[++tot]=(Node){1,pos+1,b[pos+1],i};
            }
            a[pos]=v;
            b[pos]=a[pos]==a[pos-1]?0:a[pos];
            if(b[pos]) op1[++tot]=(Node){1,pos,b[pos],i};
            if(a[pos]==a[pos+1]){
                op1[++tot]=(Node){2,pos+1,b[pos+1],i};
                b[pos+1]=0;
            }
        }
        else{
            int l,r,x,y;
            scanf("%d%d%d%d",&l,&r,&x,&y);
            op1[++tot]=(Node){3,l,x-1,i};
            op1[++tot]=(Node){4,l,y,i};
            op1[++tot]=(Node){4,r,x-1,i};
            op1[++tot]=(Node){3,r,y,i};
            if(a[l]>=x&&a[l]<=y) ans[i]++;
        }
    }
    cdq(1,tot);
    for(int i=1;i<=m;i++){
        if(ans[i]!=-1) printf("%d\n",ans[i]);
    }
}


解法2:带修主席树

这样子做是在线的,但时间空间都有点卡,稍微调了调居然抖过去了。
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int N=2e5+7;
const int inf=1e8+7;
int n,m,tot;
int rt[N],a[N],b[N],le[50],ri[50],num1,num2;
int t[N*200],ls[N*200],rs[N*200];
int lb(int x){return x&(-x);}
void upd(int &now,int l,int r,int x,int v){
    if(!now) now=++tot;
    t[now]+=v;
    if(l==r) return;
    int m=l+r>>1;
    if(x<=m) upd(ls[now],l,m,x,v);
    else upd(rs[now],m+1,r,x,v);
}
void add(int i,int x,int v){
    if(i<=0) return;
    while(i<=n){
        upd(rt[i],1,n,x,v);
        i+=lb(i);
    }
}
int nl[50][50],nr[50][50];
int que(int d,int l,int r,int L,int R){
    for(int i=1;i<=num1;i++) nl[d][i]=le[i];
    for(int i=1;i<=num2;i++) nr[d][i]=ri[i];
    int res=0;
    if(L<=l&&r<=R){
        for(int i=1;i<=num1;i++) res-=t[nl[d][i]];
        for(int i=1;i<=num2;i++) res+=t[nr[d][i]];
        return res;
    }
    int m=l+r>>1;
    if(L<=m){
        for(int i=1;i<=num1;i++) le[i]=ls[nl[d][i]];
        for(int i=1;i<=num2;i++) ri[i]=ls[nr[d][i]];
        res+=que(d+1,l,m,L,R);
    }
    if(m<R){
        for(int i=1;i<=num1;i++) le[i]=rs[nl[d][i]];
        for(int i=1;i<=num2;i++) ri[i]=rs[nr[d][i]];
        res+=que(d+1,m+1,r,L,R);
    }
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i]=a[i]==a[i-1]?0:a[i];
        if(b[i]) add(i,b[i],1);
    }
    for(int i=1,o;i<=m;i++){
        scanf("%d",&o);
        if(o==1){
            int pos,v;
            scanf("%d%d",&pos,&v);
            if(a[pos]==v) continue;
            add(pos,b[pos],-1);
            if(a[pos]==a[pos+1]){
                b[pos+1]=a[pos+1];
                add(pos+1,b[pos+1],1);
            }
            a[pos]=v;
            b[pos]=a[pos]==a[pos-1]?0:a[pos];
            if(b[pos]) add(pos,b[pos],1);
            if(a[pos]==a[pos+1]){
                add(pos+1,b[pos+1],-1);
                b[pos+1]=0;
            }
        }
        else{
            int l,r,x,y;
            scanf("%d%d%d%d",&l,&r,&x,&y);
            num1=num2=0;
            for(int j=l;j;j-=lb(j)) le[++num1]=rt[j];
            for(int j=r;j;j-=lb(j)) ri[++num2]=rt[j];
            int ans=que(1,1,n,x,y);
            if(a[l]>=x&&a[l]<=y) ans++;
            printf("%d\n",ans);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值