Codeforces 526 G Spiders Evil Plan

Description:

一棵树,有 q q q个询问,每次询问以 x x x为中心,建 y y y条河,满足河一定与 x x x连通,求每次询问的最大价值(河覆盖的路径长度)。
n , q ≤ 1 0 5 n,q\le 10^5 n,q105

Solution:

  • 首先,我们可以得到一个定理:
  • 对于一个无根树,如果它有 2 k 2k 2k个叶子节点,我们只用 k k k条路径就足以将其覆盖。所以我们就可以把问题变为:在无根树中找 2 y 2y 2y个叶子节点,是其形成一个包含点x的边权最大连通块 S S S
  • 也就是说我们以 x x x为根节点,找 2 y 2y 2y个叶子节点,使它们到形成的 S S S最大。
  • 而模拟发现,我们把这些叶子节点产生的贡献 v a l val val,选出最大的 2 y − 1 2y-1 2y1个即为答案。
  • 但是对于每个询问的 x x x都是不同的。
  • 然后,我们可以发现对于每次询问, S S S中一定会至少包含此树直径中的两端点之一。
  • 所以我们可以分别以直径两端点为根节点的树中找最大的 2 y − 1 2y−1 2y1个叶子节点的 v a l val val之和。
  • 但是这样就又有一个问题了,那就是这样写会导致 x x x可能不在 S S S之中。
  • 但这并不碍事,我们可以将其中一条最小的删掉,来连向 x x x

Code:

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;} 
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;} 

const int N=1e5+2,S=20; 

int n,q;

int qwq,head[N];
struct edge{
	int to,nxt;
	int w;
}E[N<<1];
void addedge(int x,int y,int z){E[qwq]=(edge){y,head[x],z};head[x]=qwq++;}
#define EREP(x) for(int i=head[x];~i;i=E[i].nxt)

int Mx,Id;

void dfs(int x,int f,int dis){
	if(chkmax(Mx,dis)) Id=x;
	EREP(x){
		int y=E[i].to;
		if(y==f)continue;
		dfs(y,x,dis+E[i].w);
	}
}

int val_cmp[N];
bool cmp(int x,int y){return val_cmp[x]>val_cmp[y];}

struct node{
	int rt;
	int far[N],dis[N];
	int fa[N][S];
	int cnt,leaf[N],val[N],sum[N],id[N];
	
	void dfs1(int x,int f){
		fa[x][0]=f;
		bool child=0;
		EREP(x){
			int y=E[i].to;
			if(y==f)continue;
			child=1;
			dis[y]=dis[x]+E[i].w;
			dfs1(y,x);
			if(dis[far[x]]<dis[far[y]]) far[x]=far[y];
		}
		if(!child)leaf[++cnt]=x,far[x]=x;
	}
	
	void dfs2(int x,int v){
		val[x]=v;
		EREP(x){
			int y=E[i].to;
			if(y==fa[x][0])continue;
			if(far[x]==far[y]) dfs2(y,v+E[i].w);
			else dfs2(y,E[i].w);
		}
	}
	
	void init(){
		dfs1(rt,0);
		SREP(j,1,S) REP(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];
		
		dfs2(rt,0);
		memcpy(val_cmp,val,sizeof val_cmp);
		sort(leaf+1,leaf+1+cnt,cmp);
		REP(i,1,cnt) id[leaf[i]]=i,sum[i]=sum[i-1]+val[leaf[i]];
	}
	
	int solve(int x,int y){
		if(y>=cnt) return sum[cnt];
		if(y>=id[far[x]]) return sum[y];
		
		int t=far[x];
		
		DREP(i,S-1,0) if(fa[x][i] and id[far[fa[x][i]]]>y) x=fa[x][i];
		x=fa[x][0];
		
		return sum[y]+dis[t]-dis[x]-min(dis[far[x]]-dis[x],val[leaf[y]]);
	}
	
}A,B;

void Init(){
	Mx=0;
	dfs(1,0,0);
	A.rt=Id;
	Mx=0;
	dfs(Id,0,0);
	B.rt=Id;
	A.init();
	B.init();
}
 
int main(){
//	freopen("farm.in","r",stdin);
//	freopen("farm.out","w",stdout);
	scanf("%d%d",&n,&q);
	memset(head,-1,sizeof head);
	SREP(i,1,n){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		addedge(a,b,c);
		addedge(b,a,c);
	}
	
	Init();
	
	while(q--){
		int x,y;
		scanf("%d%d",&x,&y);
		y=y*2-1;
		int ans=0;
		chkmax(ans,max(A.solve(x,y),B.solve(x,y)));
		printf("%d\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值