[CF526G]Spiders Evil Plan

Description

给出一棵n个点的树,边有边权
q次询问每次询问给出点x和y,问选y条路径,满足这y条路径的并是一个连通块S,S包含x,且S内的边权和尽量大
输出最大的边权和
强制在线
n,q<=10^5

Solution

先考虑一次询问
我们可以以x为根,然后选2y个叶子到根的路径最长
容易发现直径的某个端点一定被选,我们可以以某个直径端点为根建树
不考虑包含x,问题变成选2y-1个叶子到根的路径并最大
贪心地选,每次选择贡献最大的叶子,可以发现方案类似于长链剖分
接下来考虑把x加入答案,有两种情况:
1:删去最小的叶子,将x加入
2:将x加入,断掉和x相交的那个叶子
倍增找到x相交的叶子讨论一下即可

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=lst[a];i;i=nxt[i])
using namespace std;

int read() {
	char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
	int x=ch-'0';
	for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x;
}

void write(int x) {
	if (!x) {puts("0");return;}
	char ch[20];int tot=0;
	for(;x;x/=10) ch[++tot]=x%10+'0';
	fd(i,tot,1) putchar(ch[i]);
	puts("");
}

const int N=1e5+5;

int t[N<<1],nxt[N<<1],v[N<<1],lst[N],l;
void add(int x,int y,int z) {t[++l]=y;v[l]=z;nxt[l]=lst[x];lst[x]=l;}

int n,m,dis[N],a,b;

void dfs(int x,int y) {
	rep(i,x)
		if (t[i]!=y) {
			dis[t[i]]=dis[x]+v[i];
			dfs(t[i],x);
		}
}

struct Tree{
	int fa[N][17];
	int mx[N],sum[N],son[N],an[N],id[N],m;
	pair<int,int> val[N];

	void dfs(int x,int y) {
		fa[x][0]=y;mx[x]=sum[x];
		rep(i,x)
			if (t[i]!=y) {
				sum[t[i]]=sum[x]+v[i];
				dfs(t[i],x);
				if (mx[t[i]]>mx[x]) {
					son[x]=t[i];
					mx[x]=mx[t[i]];
				}
			}
	}

	void make(int x,int y) {
		if (!son[x]) {
			val[++m]=make_pair(-y,x);
			return;
		}
		rep(i,x) if (t[i]!=fa[x][0]) make(t[i],(t[i]==son[x]?y:0)+v[i]);
	}

	void build(int x) {
		dfs(x,0);
		fo(j,1,16) fo(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];
		make(x,0);
		sort(val+1,val+m+1);
		fo(i,1,m) {
			int x=val[i].second;
			for(;x&&!id[x];x=fa[x][0]) id[x]=i;
			an[i]=an[i-1]-val[i].first;
		}
	}

	int solve(int x,int y) {
		y=2*y-1;if (y>m) y=m;
		if (id[x]<=y) return an[y];
		int z=x;
		fd(j,16,0) if (id[fa[z][j]]>y) z=fa[z][j];
		z=fa[z][0];
		return an[y]+max(mx[x]-mx[z],mx[x]-sum[z]+val[y].first);
	}
}t1,t2;

int main() {
	n=read();m=read();
	fo(i,1,n-1) {
		int x=read(),y=read(),z=read();
		add(x,y,z);add(y,x,z);
	}
	dis[1]=0;dfs(1,0);fo(i,1,n) if (dis[i]>dis[a]) a=i;
	dis[a]=0;dfs(a,0);fo(i,1,n) if (dis[i]>dis[b]) b=i;
	t1.build(a);t2.build(b);
	int ans=0;
	for(;m;m--) {
		int x=(read()+ans-1)%n+1,y=(read()+ans-1)%n+1;
		write(ans=max(t1.solve(x,y),t2.solve(x,y)));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值