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 到根的部分选上的,直接考虑暴力跳长剖就行了吧(倍增)。
    */
    
  • 相关阅读:
    把git项目放到个人服务器上
    关于fcitx无法切换输入法的问题解决
    博客变迁通知
    (欧拉回路 并查集 别犯傻逼的错了) 7:欧拉回路 OpenJudge 数据结构与算法MOOC / 第七章 图 练习题(Excercise for chapter7 graphs)
    (并查集) HDU 1856 More is better
    (并查集 不太会) HDU 1272 小希的迷宫
    (并查集 注意别再犯傻逼的错了) HDU 1213 How Many Tables
    (最小生成树 Kruskal算法) 51nod 1212 无向图最小生成树
    (并查集) HDU 1232 畅通工程
    (最小生成树 Prim) HDU 1233 还是畅通工程
  • 原文地址:https://www.cnblogs.com/Point-King/p/15217195.html
Copyright © 2011-2022 走看看