IOI2021集训队作业129CF Longest Rivers

给出一棵树,对于每个叶子节点\(x\),求:在所有的树链剖分方案中,求包含\(x\)的链的最优排名(长度从大到小排序,排名越靠前越优)。

\(n\le 10^6\)


考虑求出一个叶子\(x\)的答案。首先显然要划分出\(rt\to x\),记这条链的长度为\(L\)

定义\(len_i\)表示从\(i\)子树中延伸上去的链长(包括\(i\)到父亲那段),\(mx_i\)\(i\)子树中的最长链。

假如希望\(x\)能排到rank1,可以如此贪心:做到某个不被链\(rt\to x\)经过的子树\(u\)时,对于所有\(v\in son(u)\),尝试找到最小的\(len_v\)并接在边\((u,fa_u)\)下方。如果一直可以满足\(len_u\le L\),那么确实可以排到rank1。在做的过程中,如果出现了一次\(len_u>L\),那么就让它直接向上延伸到和\(rt\to x\)相交为止。

整理一下思路,可以发现其实就是求:在\(rt\to x\)以外的子树中,极小的满足\(mx_u>L\)的子树\(u\)的个数。另外可以发现对于任意\(u\in rt\to x\)\(len_u\le L\),所以都不可能是极小的\(u\)。所以可以把范围从\(rt\to x\)以外的子树变成所有的子树。

于是按照询问的叶子深度排序,每次只保留\(mx_u>L\)的点,此时的叶子个数就是答案。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 1000005
#define ll long long
#define INF 1000000000000000
int n,m;
char str[N][13];
struct EDGE{
	int to,w;
	EDGE *las;
} e[N];
int ne;
EDGE *last[N];
int deg[N];
void link(int u,int v,int w){
	e[ne]={v,w,last[u]};
	last[u]=e+ne++;
	deg[u]++;
}
int fa[N];
ll dep[N],len[N],mx[N];
int in[N],out[N],cnt;
void dfs(int x){
//	in[x]=++cnt;
	if (x>m){
		mx[x]=len[x]=0;
//		out[x]=cnt;
		return;
	}
	len[x]=INF;
	for (EDGE *ei=last[x];ei;ei=ei->las){
		dep[ei->to]=dep[x]+ei->w;
		fa[ei->to]=x;
		dfs(ei->to);
		len[ei->to]+=ei->w;
		mx[ei->to]=max(mx[ei->to],len[ei->to]);	
		len[x]=min(len[x],len[ei->to]);
		mx[x]=max(mx[x],mx[ei->to]);
	}
//	out[x]=cnt;
}
int p[N];
bool cmpp(int a,int b){return dep[a+m]<dep[b+m];}
int q[N],nq;
int cmpq(int a,int b){return mx[a]>mx[b];}
int ans[N];
void cut(ll L){
	while (nq){
		int x=q[0];
		if (mx[x]>L) return;
		pop_heap(q,q+nq--,cmpq);
		if (fa[x]){
			int y=fa[x];
			if (--deg[y]==0){
				q[nq++]=y;
				push_heap(q,q+nq,cmpq);
			}
		}
	}
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1,x,w;i<=n;++i){
		scanf("%s%d%d",str[i],&x,&w);
		link(x,i+m,w);
	}
	for (int i=1,x,w;i<=m;++i){
		scanf("%d%d",&x,&w);
		link(x,i,w);
	}
	dfs(0);
	for (int i=1;i<=n;++i)
		p[i]=i;
	sort(p+1,p+n+1,cmpp);
	for (int i=0;i<=m+n;++i)
		if (deg[i]==0)
			q[nq++]=i;
	make_heap(q,q+nq,cmpq);
	for (int i=1;i<=n;++i){
		cut(dep[p[i]+m]);
		ans[p[i]]=nq;
	}
	for (int i=1;i<=n;++i)
		printf("%s %d\n",str[i],ans[i]+1);
	return 0;
}
posted @ 2020-11-28 16:22  jz_597  阅读(112)  评论(0编辑  收藏  举报