20260612模拟赛
20260612模拟赛
红蓝树
题面:
有一个栈,栈有两种状态,无色和蓝色,有三种操作,
1.将一棵红树苗加入栈顶,若栈为蓝色,则将此棵红树苗变为蓝树苗,并将栈变为无色。
2.将一棵蓝树苗加入栈顶,若栈为蓝色,则将栈变为无色。
3.将栈顶的树苗弹出,若树苗为蓝色,则将栈变为蓝色。如果在弹出前栈已经为空,则什么都不会发生。
小 T 有一个长为 \(n\) 的操作序列 \(a\),其中 \(a_i\in\{1,2,3\}\) 代表了第 \(i\) 个操作是上述的第几种操作。
小 T 有 \(m\) 次修改或询问,形如:
- 1 l r 表示对所有 \(i\in[l,r]\),若 \(a_i=1\) 则改为 2,否则若 \(a_i=2\) 则改为 1。
- 2 l r 表示从空栈开始,依次进行操作 \(a_l,a_{l+1},\ldots,a_r\),询问这个过程中一共有多少蓝色的树苗。
\(n\leq 600000\)
题解:
首先括号匹配找到每个树苗被弹出的时刻。那么一棵树苗 \(u\) 只可能对它弹出之后的第一个加入的树苗 \(v\) 产生影响,连有向边 \((u,v)\)。特别地,若之后没有加入树苗则向 \(n+1\) 连边。
得到一棵树,每棵蓝树苗都会让它的所有祖先变蓝。给定询问区间 \([l,r]\) 之后,相当于是把 \([l,r]\) 中加入的树苗拿出来,在它们对应的点在树上的导出子图上询问。
加入树苗的顺序,也正好对应了树上 dfs 序的倒序。假设最后弹出的树苗形如 \(.......(.........)\),其中加入/弹出的时刻为左/右括号,可以在树上先 dfs 括号里,再 dfs 这个树苗,以及它左边的子树。总可以在末尾添加 3 操作使得所有树苗均被弹出。
题意变为在一棵树上,有红点和蓝点,支持 dfs 序区间翻转颜色,以及给出 dfs 序区间 \([l,r]\) ,查询这些点构成的森林对应的答案。如果询问时的点构成了一棵连通子树,那么答案是蓝点与根构成的虚树的大小,相邻两蓝点距离和的一半。
但 dfs 序区间可以不连通,我们需要想办法把它变成连通的。记 dfs 序为 \(l-1\) 的点为 \(u\),如果再加入 \(u\) 到根的路径上的所有点,那么这些点即可构成连通子树。考虑消除加入的链的影响,链上有一个前缀是蓝色的,第一次变蓝的位置是点 \(u\) 与区间 \([l,r]\) 中 dfs 序最小的蓝点的 LCA。
线段树维护区间 \([l,r]\) 相邻蓝点距离,dfs 序最小最大蓝点即可。
代码
#include<bits/stdc++.h>
#define ll long long
#define fir first
#define sec second
using namespace std;
inline int read(){
int s=0,k=1;
char c=getchar();
while(c>'9'||c<'0'){
if(c=='-') k=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
s=(s<<3)+(s<<1)+(c^48);
c=getchar();
}
return s*k;
}
const int N=6e5+5;
int n,a[N],pre[N],suf[N],dep[N],st[22][N<<1],lgn[N<<1],tim,m,ids[N],id[N],dfn[N],Q;
vector<int>e[N];
struct node{
int s,l,r;
};
struct Tree{
node v,s;
int tag;
}t[N<<2];
void dfs(int x){
st[0][++tim]=x;
ids[x]=tim;
for(int v:e[x]){
dep[v]=dep[x]+1;
dfs(v);
st[0][++tim]=x;
}
}
int cmin(int x,int y){
return dep[x]<dep[y]?x:y;
}
int LCA(int x,int y){
int l=ids[x],r=ids[y];
if(l>r) swap(l,r);
int mn=lgn[r-l+1];
return cmin(st[mn][l],st[mn][r-(1<<mn)+1]);
}
int Dis(int x,int y){
return dep[x]+dep[y]-2*dep[LCA(x,y)];
}
node pushup(node L,node R){
if(!L.l&&!L.r) return R;
if(!R.l&&!R.r) return L;
node S;
S.l=L.l;S.r=R.r;
S.s=L.s+R.s+Dis(L.r,R.l);
return S;
}
void pushup(int s){
t[s].v=pushup(t[s<<1].v,t[s<<1|1].v);
t[s].s=pushup(t[s<<1].s,t[s<<1|1].s);
}
void upd(int s){
t[s].tag^=1;
swap(t[s].s,t[s].v);
}
void pushdown(int s){
if(t[s].tag){
upd(s<<1);
upd(s<<1|1);
t[s].tag=0;
}
}
void build(int s,int l,int r){
if(l==r){
t[s].v={0,0,0};
t[s].s={0,id[l],id[l]};
if(a[id[l]]==2) swap(t[s].s,t[s].v);
return ;
}
int mid=l+r>>1;
build(s<<1,l,mid);
build(s<<1|1,mid+1,r);
pushup(s);
}
void update(int s,int l,int r,int L,int R){
if(L<=l&&r<=R){
upd(s);
return ;
}
pushdown(s);
int mid=l+r>>1;
if(L<=mid) update(s<<1,l,mid,L,R);
if(R>mid) update(s<<1|1,mid+1,r,L,R);
pushup(s);
}
node query(int s,int l,int r,int L,int R){
if(L<=l&&r<=R) return t[s].v;
pushdown(s);
int mid=l+r>>1;
if(R<=mid) return query(s<<1,l,mid,L,R);
if(L>mid) return query(s<<1|1,mid+1,r,L,R);
return pushup(query(s<<1,l,mid,L,R),query(s<<1|1,mid+1,r,L,R));
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read();Q=read();
for(int i=1;i<=n;i++) a[i]=read();
{
vector<int>vec;
stack<int>st;
for(int i=1;i<=n;i++){
if(a[i]==3){
if(st.size()){
vec.push_back(st.top());
st.pop();
}
}
else{
e[i]=vec;
vec.clear();
st.push(i);
}
}
e[n+1]=vec;
while(st.size()){
e[n+1].push_back(st.top());
st.pop();
}
}
dfn[n+1]=++m;id[m]=n+1;
for(int i=n;i>=1;i--)
if(a[i]!=3){
dfn[i]=++m;
id[m]=i;
}
for(int i=1,lst=0;i<=n;i++){
if(a[i]!=3) lst=i;
pre[i]=lst;
}
for(int i=n,lst=n+1;i>=1;i--){
if(a[i]!=3) lst=i;
suf[i]=lst;
}
dfs(n+1);
for(int i=2;i<=tim;i++) lgn[i]=lgn[i>>1]+1;
for(int j=1;j<=lgn[tim];j++)
for(int i=1;i+(1<<j)-1<=tim;i++)
st[j][i]=cmin(st[j-1][i],st[j-1][i+(1<<j-1)]);
build(1,1,m);
while(Q--){
int op=read(),l=read(),r=read();
l=dfn[suf[l]];r=dfn[pre[r]];
swap(l,r);
if(l>r){
if(op==2) printf("0\n");
continue;
}
if(op==1) update(1,1,m,l,r);
else{
int u=id[l-1];
node v=query(1,1,m,l,r);
if(!v.l&&!v.r) printf("0\n");
else printf("%d\n",(v.s+dep[v.l]+dep[v.r])/2-dep[LCA(u,v.l)]);
}
}
return 0;
}

浙公网安备 33010602011771号