zoukankan      html  css  js  c++  java
  • CF526G Spiders Evil Plan

    非常不错的一道题。

    题解

    首先我们考虑没有 (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
    ",x,y);
    		printf("%d
    ",lstans=max(solve(x,2*y-1,0),solve(x,2*y-1,1)));
    	}
    	return 0;
    }
    /*
    应该是考虑以直径的两端点为根做长剖?
    这样整棵树就被我们分成若干条长链,选取其中的最长的 2y-1 条即可?
    但是考虑到这样选出来的连通块可能不包含 x ,这个是需要特判的。
    考虑到我们是需要将 x 到根的部分选上的,直接考虑暴力跳长剖就行了吧(倍增)。
    */
    
  • 相关阅读:
    JS时间自动更新
    浏览器一般兼容问题
    实现笛卡尔心形线的重复循环绘制
    js判断是否为ie6以外的浏览器,若是,则调用相应脚本
    html+css+js实现标签页切换
    实现跨浏览器的背景渐变
    IE8支持HTML5的占位符placeholder
    JS打造的跟随鼠标移动的酷炫拓扑图案
    JS获取阴历阳历和星期
    svn up时提示跳过某节点
  • 原文地址:https://www.cnblogs.com/Point-King/p/15217195.html
Copyright © 2011-2022 走看看