题解:P14164 [ICPC 2022 Nanjing R] 命题作文
LastKismet · · 题解
分三种情况讨论:
- 选一个未被覆盖的链边和一条剩下的边,简单维护一下未被覆盖的链边集合即可,为了在情况三不算重另一条边不应也是未被覆盖的链边。
- 选一个恰被覆盖一次的链边和覆盖他的边,同样简单维护一下被覆盖一次的链边即可。
- 选两个覆盖情况完全一致的链边。
显然前两种拿一个 set 就做完了。
对于第三种,考虑直接大力维护覆盖集合,对每种覆盖集合顺序维护下标,每次修改枚举影响到的覆盖集合,使用平衡树维护修改,修改次数是
那么只需考虑如何找发生了修改的集合,也就是同时在区间内外都有元素的集合,我们对每个点维护其同色的前驱和后继,挂线段树上维护区间最值即可,复杂度也是
const int N=2.5e5+5;
int n,m;
set<int> s0,s1;
ll ans;
int tot,col[N];
int rt[N],ls[N],rs[N],sz[N],fa[N];
ll pri[N];auto rnd=mt19937_64(1017);
il void upd(int x){sz[x]=sz[ls[x]]+1+sz[rs[x]],fa[x]=0,fa[ls[x]]=fa[rs[x]]=x;}
il void pud(int x){col[ls[x]]=col[rs[x]]=col[x];}
int merge(int x,int y){
if(!x||!y)return x|y;
if(pri[x]>pri[y])return pud(x),rs[x]=merge(rs[x],y),upd(x),x;
else return pud(y),ls[y]=merge(x,ls[y]),upd(y),y;
}
void split(int v,int x,int &rta,int &rtb){
if(!x)return rta=rtb=0,void();
pud(x);
if(x<v)split(v,rs[x],rs[x],rtb),rta=x;
else split(v,ls[x],rta,ls[x]),rtb=x;
upd(x);
}
il int gc(int x){
while(fa[x])x=fa[x];
return col[x];
}
il int lf(int x){
while(ls[x])x=ls[x];
return x;
}
il int ri(int x){
while(rs[x])x=rs[x];
return x;
}
int pre[N],nxt[N];
int dp[N<<2],dn[N<<2];
il int cp(int a,int b){return pre[a]<pre[b]?a:b;}
il int cn(int a,int b){return nxt[a]>nxt[b]?a:b;}
il void upd1(int x){dp[x]=cp(dp[x<<1],dp[x<<1|1]),dn[x]=cn(dn[x<<1],dn[x<<1|1]);}
void build(int x=1,int l=1,int r=n){
if(l==r)return dp[x]=dn[x]=l,void();
int m=l+r>>1;build(x<<1,l,m),build(x<<1|1,m+1,r);
upd1(x);
}
void update(int k,int x=1,int l=1,int r=n){
if(l==r||!k)return ;
int m=l+r>>1;k<=m?update(k,x<<1,l,m):update(k,x<<1|1,m+1,r);
upd1(x);
}
int qp(int lq,int rq,int x=1,int l=1,int r=n){
if(lq<=l&&r<=rq)return dp[x];
int m=l+r>>1;
if(rq<=m)return qp(lq,rq,x<<1,l,m);
if(m<lq)return qp(lq,rq,x<<1|1,m+1,r);
return cp(qp(lq,rq,x<<1,l,m),qp(lq,rq,x<<1|1,m+1,r));
}
int qn(int lq,int rq,int x=1,int l=1,int r=n){
if(lq<=l&&r<=rq)return dn[x];
int m=l+r>>1;
if(rq<=m)return qn(lq,rq,x<<1,l,m);
if(m<lq)return qn(lq,rq,x<<1|1,m+1,r);
return cn(qn(lq,rq,x<<1,l,m),qn(lq,rq,x<<1|1,m+1,r));
}
int T;
il ll calc0(){return s0.size()*(n-1+T-s0.size());}
il ll calc1(){return s1.size();}
il ll calc2(ll v){return v*(v-1)/2;}
void INIT(){}
void MAIN(){
read(n,m);
s0.clear(),s1.clear();
repl(i,1,n)s0.insert(i);
T=0;ans=calc0()+calc2(n-1);
rt[1]=0;
tot=1;repl(i,1,n)pri[i]=rnd(),ls[i]=rs[i]=0,sz[i]=1,col[i]=1,rt[1]=merge(rt[1],i);
pre[1]=1;repl(i,2,n)pre[i]=i-1;
nxt[n-1]=n-1;per(i,n-2,1)nxt[i]=i+1;
build();
rep(i,1,m){
int l,r;read(l,r);
if(l>r)swap(l,r);--r;
auto it=s0.begin();
ans-=calc0()+calc1();
while((it=s1.lower_bound(l))!=s1.end()&&*it<=r)s1.erase(it);
while((it=s0.lower_bound(l))!=s0.end()&&*it<=r)s1.insert(*it),s0.erase(it);
T=i;ans+=calc0()+calc1();
if(l>r){put(ans);continue;}
int t;
while(pre[t=qp(l,r)]<l||nxt[t=qn(l,r)]>r){
int c0=gc(t),c1=++tot;rt[c1]=0;
ans-=calc2(sz[rt[c0]]);
int rta,rtb;
split(l,rt[c0],rta,rt[c1]),split(r+1,rt[c1],rt[c1],rtb);
col[rt[c1]]=c1;
t=lf(rt[c1]),pre[t]=t,update(t),t=ri(rt[c1]),nxt[t]=t,update(t);
int tl=ri(rta),tr=lf(rtb);nxt[tl]=tr?tr:tl,pre[tr]=tl?tl:tr;update(tl),update(tr);
rt[c0]=merge(rta,rtb);
ans+=calc2(sz[rt[c0]])+calc2(sz[rt[c1]]);
}
put(ans);
}
}