ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF526G Spiders Evil Plan

2021-09-02 09:00:34  阅读:284  来源: 互联网

标签:tmp int CF526G tr Evil fa tag Spiders res


非常不错的一道题。

题解

首先我们考虑没有 \(x\) 的限制,如果我们选择 \(y\) 条路径,最优的选法是什么?

首先可以证明,最后的 \(y\) 条路径必然是一个连通块,因为如果不是一个连通块,必然可以通过交换两条路径的交点来合并连通块,于是最后就合并为了一个连通块。这样的话,问题就被我们转换为了选择 $ 2y$ 个叶子,使得这些叶子所包含的连通块边权最大。

我们易证得直径的一端点必然位于答案中,这样如果我们以直径的一端点为根,问题就转化为了选择 \(2y-1\) 个叶子,打通其到根的路径,问连通块边权最大多少,这是一个长剖的板题

但是考虑到有多次询问,需要预处理。

以上就是没有 \(x\) 的限制的时候该怎么做,如果考虑到 \(x\) 的限制,意味着我们需要删掉一个叶子再加入 \(x\) 所在长链的叶子节点,可以证明(反证法什么的),删掉的叶子只能是两种情况:

  1. 其是排名为 \(2y-1\) 的长链。
  2. 是加入的 \(x\) 所在长链碰到的第一个叶子。

然后用倍增暴力维护一下就行了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,q,rt1,rt2,lstans=0;
struct Edge{int nxt,to,val;}e[N<<1];int fir[N];
void add(int u,int v,int w,int i){e[i]=(Edge){fir[u],v,w},fir[u]=i;}
namespace find_diameter{
	int dep[N];
	void find(int u,int fa,int &res){
		if(dep[u]>dep[res]) res=u;
		for(int i=fir[u];i;i=e[i].nxt){
			int v=e[i].to;if(v==fa) continue;
			dep[v]=dep[u]+e[i].val,find(v,u,res);
		}
	}
	int main(){
		dep[1]=0,find(1,0,rt1);
		dep[rt1]=0,find(rt1,0,rt2);
		return 0;
	}
}
struct Node{int fa[20],sum[20],son,dep,dis,top;}tr[2][N];
int len[2],ord[2][N],sum[2][N];priority_queue<pair<int,int> > s;
void dfs1(int u,int tag){
	tr[tag][u].top=u;
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==tr[tag][u].fa[0]) continue;
		tr[tag][v].fa[0]=u,tr[tag][v].dep=tr[tag][u].dep+e[i].val;
		dfs1(v,tag),tr[tag][v].dis+=e[i].val,tr[tag][v].sum[0]=e[i].val;
		tr[tag][u].dis=max(tr[tag][u].dis,tr[tag][v].dis);
		if(tr[tag][v].dis>=tr[tag][tr[tag][u].son].dis) tr[tag][u].son=v;
	}
}
void dfs2(int u,int tag){
	if(tr[tag][u].son){
		tr[tag][tr[tag][u].son].top=tr[tag][u].top;
		dfs2(tr[tag][u].son,tag);
	}
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;if(v!=tr[tag][u].fa[0]&&v!=tr[tag][u].son) dfs2(v,tag);
	}
}
void init(){
	for(int j=1;j<20;++j){
		for(int i=1;i<=n;++i){
			tr[0][i].fa[j]=tr[0][tr[0][i].fa[j-1]].fa[j-1];
			tr[0][i].sum[j]=tr[0][i].sum[j-1]+tr[0][tr[0][i].fa[j-1]].sum[j-1];
			tr[1][i].fa[j]=tr[1][tr[1][i].fa[j-1]].fa[j-1];
			tr[1][i].sum[j]=tr[1][i].sum[j-1]+tr[1][tr[1][i].fa[j-1]].sum[j-1];
		}
	}
	while(!s.empty()) s.pop();
	for(int i=1;i<=n;++i){
		if(tr[0][i].top==i) s.push(make_pair(tr[0][i].dis,i));
	}
	while(!s.empty()){
		ord[0][s.top().second]=++len[0];
		sum[0][len[0]]=sum[0][len[0]-1]+s.top().first,s.pop();
	}
	while(!s.empty()) s.pop();
	for(int i=1;i<=n;++i){
		if(tr[1][i].top==i) s.push(make_pair(tr[1][i].dis,i));
	}
	while(!s.empty()){
		ord[1][s.top().second]=++len[1];
		sum[1][len[1]]=sum[1][len[1]-1]+s.top().first,s.pop();
	}
}
int solve1(int x,int k,int tag){
	int tmp=x,res=tr[tag][x].dis-tr[tag][tmp].sum[0];
	for(int j=19;j>=0;--j){
		if(ord[tag][tr[tag][tr[tag][tmp].fa[j]].top]>k)
		res+=tr[tag][tmp].sum[j],tmp=tr[tag][tmp].fa[j];
	}
	res+=tr[tag][tmp].sum[0],tmp=tr[tag][tmp].fa[0];
	res-=tr[tag][tmp].dis,res+=tr[tag][tmp].sum[0];
	return sum[tag][k]+res;
}
int solve2(int x,int k,int tag){
	int tmp=x,res=tr[tag][x].dis-tr[tag][tmp].sum[0];
	for(int j=19;j>=0;--j){
		if(ord[tag][tr[tag][tr[tag][tmp].fa[j]].top]>k-1)
		res+=tr[tag][tmp].sum[j],tmp=tr[tag][tmp].fa[j];
	}
	res+=tr[tag][tmp].sum[0],tmp=tr[tag][tmp].fa[0];
	return sum[tag][k-1]+res;
}
int solve(int x,int k,int tag){
	k=min(k,len[tag]);
	if(ord[tag][tr[tag][x].top]<=k) return sum[tag][k];
	return max(solve1(x,k,tag),solve2(x,k,tag));
}
int main(){
	cin>>n>>q;
	for(int i=1;i<n;++i){
		int u,v,w;scanf("%d%d%d",&u,&v,&w);
		add(u,v,w,i<<1),add(v,u,w,i<<1|1);
	}
	find_diameter::main();
	dfs1(rt1,0),dfs2(rt1,0),dfs1(rt2,1),dfs2(rt2,1),init();
	for(int i=1;i<=q;++i){
		int x,y;scanf("%d%d",&x,&y),x=(x+lstans-1)%n+1,y=(y+lstans-1)%n+1;
		// printf("%d %d\n",x,y);
		printf("%d\n",lstans=max(solve(x,2*y-1,0),solve(x,2*y-1,1)));
	}
	return 0;
}
/*
应该是考虑以直径的两端点为根做长剖?
这样整棵树就被我们分成若干条长链,选取其中的最长的 2y-1 条即可?
但是考虑到这样选出来的连通块可能不包含 x ,这个是需要特判的。
考虑到我们是需要将 x 到根的部分选上的,直接考虑暴力跳长剖就行了吧(倍增)。
*/

标签:tmp,int,CF526G,tr,Evil,fa,tag,Spiders,res
来源: https://www.cnblogs.com/Point-King/p/15217195.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有