题解 CF1623E 【Middle Duplication】(贪心,树剖)
来个不用动脑子的树剖维护做法
按中序遍历将树推平得到字符串 \(s\),从前向后遍历该字符串,设 \(nex_i\) 表示满足 \(s_i\neq s_j\) 且 \(i<j\) 的最小位置 \(j\)。若 \(s_i<s_{nex_i}\),则要尽量满足复制当前的节点,查询该节点到根的路径中有没复制的节点个数 \(v\),若 \(v\leq k\),则复制当前节点,并将到根路径上的节点全部标记为已复制:否则复制不优,直接跳到 \(nex_i\) 继续操作。
但这样做会有个问题。如果 \(i<j,s_{i}>s_{nex_i},s_j<s_{nex_j}\) 且 \(i\) 是 \(j\) 的祖先,按照上面操作会在复制 \(j\) 时把 \(i\) 复制,这样做则不优,所以当发现当前节点 \(s_i>s_{nex_i}\) 时,要扫描 \(i\leq k<nex_i\),若 \(k\) 还没有被标记复制(若被标记复制,则有 \(k1<i\) 的节点复制更优,满足字典序更小),则 ban 掉 \(k\) 的子树(让 \(k\) 的子树的节点以后扫描到时不能复制),然后再跳转。
以上操作可以用树剖维护,时间复杂度为 \(O(n\log^2 n)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define rg register
#define ll long long
#define ull unsigned ll
#define inf 0x3f3f3f3f
#define djq 1000000007
#define lowbit(x) (x&(-x))
const double alpha=0.73;
inline void file(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
}
char buf[1<<21],*p1=buf,*p2=buf;
inline int getc(){
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
rg int ret=0,f=0;char ch=getchar();
while(!isdigit(ch)){if(ch==EOF)exit(0);if(ch=='-')f=1;ch=getchar();}
while(isdigit(ch)){ret=ret*10+ch-48;ch=getchar();}
return f?-ret:ret;
}
int n,k,son[200005][2];
char s[200005];
int as[200005];
int a[200005],id,nex[200005];
int dep[200005],fa[200005],hson[200005],siz[200005],dfn[200005],top[200005],tim;
struct solve{
void dfs1(int x,int f){
if(!x) return;
fa[x]=f; dep[x]=dep[f]+1; siz[x]=1;
dfs1(son[x][0],x); siz[x]+=siz[son[x][0]];
if(siz[son[x][0]]>siz[hson[x]]) hson[x]=son[x][0];
a[++id]=x; as[id]=s[x]-'a'+1;
dfs1(son[x][1],x); siz[x]+=siz[son[x][1]];
if(siz[son[x][1]]>siz[hson[x]]) hson[x]=son[x][1];
}
void dfs2(int x,int topth){
if(!x) return;
top[x]=topth;
dfn[x]=++tim;
if(hson[x]) dfs2(hson[x],topth);
for(rg int i=0;i<2;++i){
const int y=son[x][i];
if(y==fa[x]||y==hson[x]) continue;
dfs2(y,y);
}
}
struct node{
int l,r,val;
bool tag,ban;
}t[200005<<3];
inline void up(int x){ t[x].val=t[x<<1].val+t[x<<1|1].val; }
inline void down(int x){
if(t[x].tag){
t[x<<1].val=t[x<<1|1].val=0;
t[x<<1].tag=t[x<<1|1].tag=1;
t[x].tag=0;
}
}
void build(int x,int l,int r){
t[x].l=l,t[x].r=r,t[x].tag=0,t[x].ban=0;
if(l==r) return t[x].val=1,void();
const int mid=l+r>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
up(x);
}
void modify1(int x,int sl,int sr){
if(sl<=t[x].l&&sr>=t[x].r) return t[x].val=0,t[x].tag=1,void();
const int mid=t[x].l+t[x].r>>1; down(x);
if(sl<=mid) modify1(x<<1,sl,sr);
if(sr>mid) modify1(x<<1|1,sl,sr);
up(x);
}
void modify2(int x,int sl,int sr){
if(sl<=t[x].l&&sr>=t[x].r) return t[x].ban=1,void();
const int mid=t[x].l+t[x].r>>1; down(x);
if(sl<=mid) modify2(x<<1,sl,sr);
if(sr>mid) modify2(x<<1|1,sl,sr);
up(x);
}
int query(int x,int sl,int sr){
if(sl<=t[x].l&&sr>=t[x].r) return t[x].val;
const int mid=t[x].l+t[x].r>>1; down(x);
int ret=0;
if(sl<=mid) ret+=query(x<<1,sl,sr);
if(sr>mid) ret+=query(x<<1|1,sl,sr);
return ret;
}
int ban(int x,int y){
if(t[x].l==t[x].r) return t[x].ban;
const int mid=t[x].l+t[x].r>>1; int ret=t[x].ban;
if(y<=mid) ret=max(ret,ban(x<<1,y));
else ret=max(ret,ban(x<<1|1,y));
return ret;
}
void d1(int x){ while(x) modify1(1,dfn[top[x]],dfn[x]),x=fa[top[x]]; }
void d2(int x){ modify2(1,dfn[x],dfn[x]+siz[x]-1); }
int q(int x){
int ret=0;
while(x) ret+=query(1,dfn[top[x]],dfn[x]),x=fa[top[x]];
return ret;
}
}ww;
inline void work(){
n=read(),k=read();
scanf("%s",s+1);
for(rg int i=1;i<=n;++i) son[i][0]=read(),son[i][1]=read();
ww.dfs1(1,0),ww.dfs2(1,1),ww.build(1,1,n);
int las=n+1;
for(rg int i=n;i>=1;--i){
if(as[i]!=as[i+1]) las=i+1;
nex[i]=las;
}//处理 nex。
for(rg int i=1;i<=n;){
if(as[i]>as[nex[i]]){ //若复制不优 。
for(rg int j=i;j<nex[i];++j)
if(ww.query(1,dfn[a[j]],dfn[a[j]]))
ww.d2(a[j]); //ban 掉子树。
i=nex[i]; continue;
}
if(ww.ban(1,dfn[a[i]])==1){ //若被祖先 ban 掉,则不能复制。
++i; continue;
}
const int nv=ww.q(a[i]);
if(nv<=k){ //复制。
k-=nv;
ww.d1(a[i]);
}
++i;
}
for(rg int i=1;i<=n;++i){
printf("%c",as[i]-1+'a');
if(ww.query(1,dfn[a[i]],dfn[a[i]])==0) printf("%c",as[i]-1+'a');
}//输出。
}
signed main(){
//file();
work();
return 0;
}
/*
*/