自己调崩了,思路写在代码里。
#include<bits/stdc++.h>
#define il inline
using namespace std;
typedef pair<int,int> pii;
const int N=2e5+10,M=1e6+10;
int n,m;
int id=0;
int tr[N];
int ans[N];
int pre[N];
int a[N],t[N];
set<pii> col,s[N];
struct Query{
int id;
int l,r,x;
}q[N];
struct Node{
int x,y,z;
int k,d,ans;
il bool operator<(const Node& Cyan)const{
if(x!=Cyan.x) return x<Cyan.x;
if(y!=Cyan.y) return y<Cyan.y;
return z<Cyan.z;
}
}w[M],Pw[M>>1];//M>>1: 除了最大的区间外,最长区间只有 M/2
/*
x 代表询问的位置
y 右端点
z 代表同颜色的前一个位置
k 表示属性
d 树状数组的加减(维护pre数组)
ans 记录答案
*/
il int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<3)+(x<<1)+c-48;
c=getchar();
}
return x*f;
}
il void write(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+48);
}
il int lowbit(int x){ return x&-x; }
il void add(int x,int val){ x++;
for(int i=x;i<N;i+=lowbit(i)) tr[i]+=val;
}
il int query(int x){
int res=0; x++;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
il void split(int x){
auto it=col.lower_bound({x+1,0});
int itr=it->first-1; --it;
int itl=it->first; if(x==itr) return ;
int color=it->second; col.erase(it);
col.insert({itl,color}),col.insert({x+1,color});
s[color].insert({itl,x}),s[color].insert({x+1,itr});
}
il void assgin(int l,int r,int c,int p){
split(l-1),split(r);
for(auto it=col.lower_bound({l,0});it->first<=r;){
int x=it->first,color=it->second; it++;
int nxt=it->first-1; s[color].erase({x,nxt}); col.erase({x,color});
if(x==l){ //特殊情况
int cor=(--(s[c].upper_bound({x,0})))->second;// 找到目标颜色的下标在当前位置的前一个位置
if(pre[x]!=cor){
w[++id]={p,x,pre[x],0,-1,0};
pre[x]=cor; w[++id]={p,x,pre[x],0,1,0};
}
} else{ // 普通情况
if(pre[x]!=x-1){
//说明此颜色和x的颜色不同,由于我们是区间赋值,所以把他们变成一样的color
w[++id]={p,x,pre[x],0,-1,0};
pre[x]=x-1; w[++id]={p,x,pre[x],0,1,0};
}
} x=nxt+1; nxt=(s[color].lower_bound({x,0}))->first; //下一个区间
int lst=(--s[color].lower_bound({l,0}))->second;// l的前一个区间
if(nxt>r&&nxt<=n&&pre[nxt]!=lst){
w[++id]={p,nxt,pre[nxt],0,-1,0};
pre[nxt]=lst; w[++id]={p,nxt,pre[nxt],0,1,0};
} //因为l这个区间没了,所以将他的后续区间连上前面的
} int x=(s[c].lower_bound({r+1,0}))->first; //赋值颜色的后继区间
if(x<=n&&pre[x]!=r){
w[++id]={p,x,pre[x],0,-1,0};
pre[x]=r; w[++id]={p,x,pre[x],0,1,0};
} s[c].insert({l,r}); col.insert({l,c}); x=r+1; //添加上l,r这一段新区间
if(x<=n){
int color=(col.lower_bound({x,0}))->second;// 找到在r以后的第一段区间的颜色
int lst=(--s[color].lower_bound({x,0}))->first; //找到这种颜色的上一个区间
if(pre[x]!=lst){ // 说明l-r中包含这种颜色,但l-r已被删除,所以重新赋值
w[++id]={p,x,pre[x],0,-1,0};
pre[x]=lst; w[++id]={p,x,pre[x],0,1,0};
}
}
}
il void CDQ(int l,int r){
//三维偏序:1.时间 2.右端点 3.pre
if(l==r) return ;
int mid=l+r>>1,i=l,ID=0;
CDQ(l,mid),CDQ(mid+1,r);
for(int j=mid+1;j<=r;j++){
while(i<=mid&&w[i].y<=w[j].y){
if(!w[i].k) add(w[i].z,w[i].d);
if(l!=1||r!=id) Pw[++ID]=w[i]; i++;
} if(w[j].k) w[j].ans+=query(w[j].z); if(l!=1||r!=id) Pw[++ID]=w[j];
} while(i<=mid){
if(!w[i].k) add(w[i].z,w[i].d);
if(l!=1||r!=id) Pw[++ID]=w[i]; i++;
} for(int k=l;k<=mid;k++) if(!w[k].k) add(w[k].z,-w[k].d);
if(l!=1||r!=id) for(int k=1;k<=ID;k++) w[l+k-1]=Pw[k]; //将这层的信息更新后返回上一层
}
signed main(){
int cnt; n=cnt=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read(),t[i]=a[i];
for(int i=1;i<=m;i++){
q[i].id=read(),q[i].l=read(),q[i].r=read();
if(q[i].id==1) q[i].x=read(),t[++cnt]=q[i].x;
} sort(t+1,t+cnt+1); cnt=unique(t+1,t+cnt+1)-t-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(t+1,t+cnt+1,a[i])-t;
for(int i=1;i<=m;i++) if(q[i].id==1) q[i].x=lower_bound(t+1,t+cnt+1,q[i].x)-t;
for(int i=1;i<=cnt;i++) s[i].insert({0,0}); //边界
for(int i=1;i<=n;i++){
pre[i]=(--s[a[i]].end())->second; //找到 a[i] 这种颜色最后出现的位置
w[++id]={0,i,pre[i],0,1,0};
s[a[i]].insert({i,i}),col.insert({i,a[i]});
} for(int i=1;i<=cnt;i++) s[i].insert({n+1,0}); //边界
col.insert({0,0}),col.insert({n+1,n+1}); //边界
for(int i=1;i<=m;i++){
if(q[i].id==1) assgin(q[i].l,q[i].r,q[i].x,i);
else w[++id]={i,q[i].r,q[i].l-1,1,1,0},w[++id]={i,q[i].l-1,q[i].l-1,1,-1,0};
} sort(w+1,w+id+1); CDQ(1,id);
for(int i=1;i<=id;i++) if(w[i].k) ans[w[i].x]+=w[i].ans*w[i].d;//在原来的位置上累加颜色
for(int i=1;i<=m;i++) if(q[i].id==2) write(ans[i]),puts("");
return 0;
}