先考虑单点修改怎么做。一个套路是,数区间里面所有第一次出现的数,形式化的表示是 (iin[l,r],prv_i<l)。那这是个二维数点了,要是单点修改就动态二维数点上 cdq,至于如何维护 (prv) 那就很 trivial 了,set
乱搞。
区间修改怎么做?观察 (prv) 的变化,发现 ((l,r]) 内的元素 (i) 都有 (prv_i=i-1),依据这个特殊性开始搞事情。那对某个区间,满足 (iin[l,r],i-1<l) 的元素仅可能是 (i=l),那就判一判就可以了,这很 trivial。于是就是要对 (l) 们展开维护。
那么每次区间修改,把这个区间里原本的 (l) 的信息都要删除。怎么快速删除?其实不难想到暴力删除复杂度是对的,因为每次修改只会增加一个 (l),那删除的次数也必定不超过修改的次数,这是个简单的势能分析。同时在删除、添加的时候,还要实时维护(原本)以它为前驱的后面的元素,不难发现对每个位置最多有一个,复杂度也是不用担心的。那对任意位置找前驱 / 后继就对每个值维护位置的 set
,但是值是批量批量修改的怎么办呢。再开个 set
记录相等段们,然后把区间的代表元((l))扔进之前的那个 set
即可。
搞着搞着搞出一个动态二位数点的操作序列,最后对着这个序列 cdq 一遍即可。复杂度二次对数。虽然这个操作序列异常的长(可能 (10n) 左右吧),但还是跑得很快,大概是 cdq 和 BIT 常数实在太小了。
但是这题莫名卡空间,理论上我的空间复杂度是线性(又称线性空间)但还是过不去。我采取了一些卡空间手段(但是只有最后一步起到决定性作用):把 int
能压成 short
就压,甚至可以压到 bool
/ signed char
;把装 3 个 1e6 级别的 int
的 pair<pair<int,int>,int>
用一个 long long
压缩起来然后解压;cdq 的时候不在里面开部分操作序列的 vector
,开到外面,并且换成数组。
code(细节有一点点(真的不是亿点点)多(set 维护的题就这样),写了 4k,但是卡空间不算的话是一遍过了啊)
#include<bits/stdc++.h>
using namespace std;
#define X x()
#define Y y()
#define Z z()
#define pb push_back
const int inf=999999;
int lowbit(int x){return x&-x;}
const int N=100010,QU=100010;
int n,qu;
int a[N];
struct query{
int tp,l,r,x;
}qry[QU];
vector<int> nums;
void discrete(){
sort(nums.begin(),nums.end());
nums.resize(unique(nums.begin(),nums.end())-nums.begin());
for(int i=1;i<=n;i++)a[i]=lower_bound(nums.begin(),nums.end(),a[i])-nums.begin()+1;
for(int i=1;i<=qu;i++)qry[i].x=lower_bound(nums.begin(),nums.end(),qry[i].x)-nums.begin()+1;
}
set<int> st[N+QU];
struct query0{
bool add;int x,y;signed char v;int id;
friend bool operator<(query0 z,query0 w){return z.x==w.x?(z.y<w.y):(z.x<w.x);}
};
vector<query0> qry0;
struct tup{
unsigned long long zip;
tup(int x,int y,int z){zip=(1ll*x<<40)+(1ll*y<<20)+z;}
int x()const{return zip>>40;}
int y()const{return zip>>20&((1<<20)-1);}
int z()const{return zip&((1<<20)-1);}
friend bool operator<(tup x,tup y){return x.zip<y.zip;}
};
set<tup> rg;
set<tup>::iterator in(int x){
set<tup>::iterator fd=rg.upper_bound(tup(x,inf,0));
return --fd;
}
int ans[QU];
int prv[N];
int tor(int l){return l==0?l:in(l)->Y;}
void delblk(int l,int r,int x){//updlist: st(pos), query0, rg, prv
st[x].erase(l);
qry0.pb(query0({1,l,prv[l],-1,0}));
set<int>::iterator fd=st[x].upper_bound(l);
if(fd!=st[x].end()){
qry0.pb(query0({1,*fd,prv[*fd],-1,0}));
set<int>::iterator fd0=fd--;
qry0.pb(query0({1,*fd0,prv[*fd0]=tor(*fd),1,0}));
}
}
void addblk(int l,int r,int x){//updlist: st(pos), query0, rg, prv
st[x].insert(l);rg.insert(tup(l,r,x));
set<int>::iterator fd=st[x].lower_bound(l);
qry0.pb(query0({1,l,prv[l]=tor(*--fd),1,0}));
fd++;fd++;
if(fd!=st[x].end()){
qry0.pb(query0({1,*fd,prv[*fd],-1,0}));
qry0.pb(query0({1,*fd,prv[*fd]=r,1,0}));
}
}
struct bitree{
int cnt[N];
bitree(){memset(cnt,0,sizeof(cnt));}
void add(int x,int v){
while(x<=n+1)cnt[x]+=v,x+=lowbit(x);
}
int Cnt(int x){
int res=0;
while(x)res+=cnt[x],x-=lowbit(x);
return res;
}
}bit;
query0 v[10*N];int tail;
void cdq(int l=0,int r=qry0.size()-1){
if(l==r)return;
int mid=l+r>>1;
cdq(l,mid),cdq(mid+1,r);
tail=0;
for(int i=l;i<=mid;i++)if(qry0[i].add==1)v[tail++]=qry0[i];
for(int i=mid+1;i<=r;i++)if(qry0[i].add==0)v[tail++]=qry0[i];
stable_sort(v,v+tail);
for(int i=0;i<tail;i++){
int add=v[i].add,x=v[i].y,v0=v[i].v,id=v[i].id;
if(add)bit.add(x+1,v0);
else ans[id]+=v0*bit.Cnt(x+1);
}
for(int i=0;i<tail;i++){
int add=v[i].add,x=v[i].y,v0=v[i].v;
if(add)bit.add(x+1,-v0);
}
}
int main(){
cin>>n>>qu;
for(int i=1;i<=n;i++)scanf("%d",a+i),nums.pb(a[i]);
for(int i=1;i<=qu;i++){
scanf("%d%d%d",&qry[i].tp,&qry[i].l,&qry[i].r);
if(qry[i].tp==1)scanf("%d",&qry[i].x),nums.pb(qry[i].x);
}
discrete();
for(int i=1;i<=nums.size();i++)st[i].insert(0);//很好的 trick!
for(int i=1;i<=n;i++){
qry0.pb(query0({1,i,prv[i]=*--st[a[i]].end(),1,0}));
rg.insert(tup(i,i,a[i]));
st[a[i]].insert(i);
}
int ask=0;
for(int i=1;i<=qu;i++){
int tp=qry[i].tp,l=qry[i].l,r=qry[i].r,x=qry[i].x;
if(tp==1){//updlist: st(pos), query0, rg, prv
set<tup>::iterator pl=in(l),pr=in(r);
tup PL=*pl,PR=*pr;
vector<tup> del;
for(set<tup>::iterator j=pl;;j++){
delblk(j->X,j->Y,j->Z),del.pb(*j);
if(j==pr)break;
}
for(int j=0;j<del.size();j++)rg.erase(del[j]);
if(l!=PL.X)addblk(PL.X,l-1,PL.Z);
addblk(l,r,x);
if(r!=PR.Y)addblk(r+1,PR.Y,PR.Z);
}
else{
ask++;
tup pl=*in(l);
ans[ask]=l!=pl.X;
qry0.pb(query0({0,r,l-1,1,ask})),qry0.pb(query0({0,l-1,l-1,-1,ask}));
}
}
cdq();
for(int i=1;i<=ask;i++)printf("%d
",ans[i]);
return 0;
}
好像并不是很难的吼)