题解 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;
}
/*
*/
posted @ 2022-02-09 16:38  Legitimity  阅读(32)  评论(0编辑  收藏  举报