题目链接https://nanti.jisuanke.com/t/41356
题意
给出一个序列,有两个操作
- 修改一个数字
- 询问一段区间内数字在一定范围内的最长连续段有多少。
题解
按照题解的思路把原数组
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[i−1],否则
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);
}
}
}