• luogub P4886 快递员(点分治)


    记得是9月月赛题,当时做的时候觉得跟ZJOI2015幻想乡战略游戏那道题很像???,就写了,然后就写挂了。。。
    我们发现假设当前点为根,我们算出(m)次询问中最远的(a)对点,如果这(a)对点,全部都两个点在根的不同子树中。当前点就是最优的就是答案。当全部(a)对点都在一个子树中,我们把答案改为那个子树对应的儿子,答案会变优。当有几对点在一个子树,另外几对点在另外的子树中,当前答案还是最优的。
    所以本题的一个想法就是,一个一个的改变根使答案变优。
    但是上述想法要求我们每一次移动一个点都要遍历整棵树。是在太慢了。
    我们考虑用点分治的方法优化算法。当全部(a)对点都在一个子树中时,一个更优的答案在那个子树中,我们找到这个子树的重心当作根。这样最多遍历(logn)次。把复杂度变为了(O(mlogn))。至此本题得到完美解决。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int N=101000;
    int cnt,head[N];
    int g[N],size[N],all,root,dis[N],ans1[N],ans2[N],dep[N],fa[N][25],vis[N];
    int n,m,a[N],b[N],ans;
    struct edge{
    	int to,nxt,w;
    }e[N*2];
    void add_edge(int u,int v,int w){
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return sum*f;
    }
    void getroot(int u,int f){
    	g[u]=0;size[u]=1;
    	for(int i=head[u];i;i=e[i].nxt){
    		int v=e[i].to;
    		if(vis[v]||f==v)continue;
    		getroot(v,u);
    		size[u]+=size[v];
    		g[u]=max(g[u],size[v]);
    	}
    	g[u]=max(g[u],all-g[u]);
    	if(g[u]<g[root])root=u;
    }
    void getdis(int u,int f,int w){
    	dep[u]=dep[f]+1;
    	fa[u][0]=f;
    	dis[u]=w;
    	for(int i=1;i<=20;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
    	for(int i=head[u];i;i=e[i].nxt){
    		int v=e[i].to;
    		if(v==f)continue;
    		getdis(v,u,w+e[i].w);
    	}
    }
    int getlca(int x,int y){
    	if(dep[x]<dep[y])swap(x,y);
    	for(int i=20;i>=0;i--)
    		if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
    	if(x==y)return x;
    	for(int i=20;i>=0;i--)
    		if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    	return fa[x][0];
    }
    int up(int u,int x){
    	for(int i=20;i>=0;i--)
    		if((x>>i)&1)u=fa[u][i],x-=(1<<i);
    	return u;
    }
    int calc(int u){
    	getdis(u,0,0);
    	int tmp=0;
    	for(int i=1;i<=m;i++){
    		int x=dis[a[i]]+dis[b[i]];
    		if(x>tmp){
    			cnt=0;
    			ans1[++cnt]=a[i];ans2[cnt]=b[i];
    			tmp=x;
    		}
    	}
    	ans=min(ans,tmp);
    	tmp=0;
    	for(int i=1;i<=cnt;i++){
    		int x=up(ans1[i],dep[ans1[i]]-dep[u]-1);
    		int y=up(ans2[i],dep[ans2[i]]-dep[u]-1);
    		if(x==y){
    			if(tmp==0)tmp=x;
    			else return -1;
    		}
    	}
    	if(tmp==-1)return -1;
    	return tmp;
    }
    void work(int u){
    	int x=calc(u);
    	if(x==-1)return;vis[u]=1;
    	for(int i=head[u];i;i=e[i].nxt){
    		int v=e[i].to;
    		if(vis[v])continue;
    		if(v==x){
    			root=0;all=size[v];
    			getroot(v,0);
    			work(root);
    		}
    	}
    }
    int main(){
    	n=read();m=read();
    	for(int i=1;i<n;i++){
    		int u=read(),v=read(),w=read();
    		add_edge(u,v,w);add_edge(v,u,w);
    	}
    	for(int i=1;i<=m;i++)a[i]=read(),b[i]=read();
    	ans=1e9;
    	g[0]=n+10,all=n;
    	getroot(1,0);work(root);
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    基于k8s搭建微服务日志收集中心
    分析java堆内存满时那些类占用内存居多
    yizimi 在 DMG 的板子库 (数据结构与算法)
    Contest 3/14
    基础算法训练1
    图论基础训练
    2021.03.09随笔
    树莓派 4B 安装 openEuler
    Docker 学习准备
    关于备案第二个服务器时遇到的问题
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/10134120.html
Copyright © 2020-2023  润新知