• 【BZOJ3784】树上路径


    题目大意

    给定一个(N)个结点的树,结点用正整数(1..N)编号。每条边有一个正整数权值。用(d(a,b))表示从结点(a)到结点(b)路边上经过边的权值。其中要求(a < b.)将这(n*(n-1)/2)个距离从大到小排序,输出前(M)个距离值。

    题目分析

    统计树上路径的问题显然需要淀粉质(好毒瘤啊,连续考了两天点分治)。

    由于前(M)大路径难以直接统计,而我们又很擅长统计长度大于(l)的路径个数,因此考虑首先二分答案求出第(M)大路径的长度(l),再统计长度大于(l)的路径。

    分析一波时间复杂度,点分(O(n logn)),统计路径时为了方便,我们需要排序后二分(又一个(logn)),本身二分答案就是(logn),这样一来时间复杂度达到了(O(n log^3n)),难以接受。

    考试的时候想到一些小优化,如把点分后的每一个子树的根记录下来。当时也想到了把路径存下来,但不知道为什么就认为空间复杂度不正确立马否定掉了。实际上就是要将路径存下来,而且空间复杂度为(O(n logn))。这样我们只需要最初建好点分树并排好序,每次只需要对每一个点为根的路径二分一下(O(n logn)),如此一来算法的时间复杂度瓶颈为(O(n log^2n)),本题得到完美解决。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int Maxn=50005;
    int n,m,h[Maxn],G[Maxn];
    int rt,root,totsize,size[Maxn],mx[Maxn];
    int lim,path[Maxn];ll ans;
    vector<int>v[Maxn],g[Maxn],Ans;
    bool vis[Maxn];
    struct edge{int to,next,w;}e[Maxn*2],f[Maxn*2];
    int getint(){
    	int w=0,f=1;char ch=getchar();
    	while(!isdigit(ch))ch!='-'?:f=-1,ch=getchar();
    	while(isdigit(ch))w=w*10+ch-'0',ch=getchar();
    	return w*f;
    }
    void addedge(int x,int y,int z){
    	static int cnt;
    	e[++cnt]=(edge){y,h[x],z};h[x]=cnt;
    }
    void AddEdge(int x,int y){
    	static int cnt;
    	f[++cnt]=(edge){y,G[x]};G[x]=cnt;
    }
    void getroot(int x,int fa){
    	size[x]=1;mx[x]=0;
    	for(int i=h[x];i;i=e[i].next){
    		int y=e[i].to;
    		if(vis[y]||y==fa)continue;
    		getroot(y,x);
    		size[x]+=size[y];
    		mx[x]=max(mx[x],size[y]);
    	}
    	mx[x]=max(mx[x],totsize-size[x]);
    	if(mx[x]<mx[root])root=x;
    }
    void getsize(int x,int fa){
    	totsize++;
    	for(int i=h[x];i;i=e[i].next){
    		int y=e[i].to;
    		if(vis[y]||y==fa)continue;
    		getsize(y,x);
    	}
    }
    void dfs(int x,int fa,int dis,vector<int>&v){
    	v.push_back(dis);
    	for(int i=h[x];i;i=e[i].next){
    		int y=e[i].to;
    		if(vis[y]||y==fa)continue;
    		dfs(y,x,dis+e[i].w,v);
    	}
    }
    void Build(int x){
    	vis[x]=1;v[x].push_back(0);
    	for(int i=h[x];i;i=e[i].next){
    		int y=e[i].to;
    		if(vis[y])continue;
    		dfs(y,x,e[i].w,v[x]);
    	}
    	sort(v[x].begin(),v[x].end());
    	for(int i=h[x];i;i=e[i].next){
    		int y=e[i].to;
    		if(vis[y])continue;
    		totsize=root=0;
    		getsize(y,x);getroot(y,x);
    		dfs(y,x,e[i].w,g[root]);
    		sort(g[root].begin(),g[root].end());
    		AddEdge(x,root);Build(root);
    	}
    }
    ll calc(vector<int>&v){
    	ll ret=0;
    	for(int i=0;i<v.size();i++)ret+=v.end()-lower_bound(v.begin(),v.end(),lim-v[i]);
    	return ret;
    }
    void solve(int x){
    	ans+=calc(v[x]);
    	for(int i=G[x];i;i=f[i].next){
    		int y=f[i].to;
    		ans-=calc(g[y]);
    	}
    	if(ans>=m)return;
    	for(int i=G[x];i;i=f[i].next){
    		int y=f[i].to;
    		solve(y);
    	}
    }
    bool Judge(int mid){
    	lim=mid;ans=0;
    	solve(rt);
    	return ans>=m*2;
    }
    void dfs2(int x,int fa,int dis){
    	path[++path[0]]=dis;
    	for(int i=h[x];i;i=e[i].next){
    		int y=e[i].to;
    		if(vis[y]||y==fa)continue;
    		dfs2(y,x,dis+e[i].w);
    	}
    }
    void Count(int x){
    	path[0]=0;path[++path[0]]=0;
    	for(int i=h[x];i;i=e[i].next){
    		int y=e[i].to;
    		if(vis[y])continue;
    		int now=path[0];
    		dfs2(y,x,e[i].w);
    		for(int j=now+1;j<=path[0];j++){
    			int pos=lower_bound(path+1,path+now+1,lim-path[j])-path;
    			for(int k=pos;k<=now;k++)Ans.push_back(path[j]+path[k]);
    		}
    		sort(path+now+1,path+path[0]+1);
    		inplace_merge(path+1,path+now+1,path+path[0]+1);
    	}
    }
    void solve2(int x){
    	vis[x]=1;Count(x);
    	for(int i=G[x];i;i=f[i].next){
    		int y=f[i].to;
    		solve2(y);
    	}
    }
    int main(){
    	n=getint();m=getint();
    	for(int i=1;i<n;i++){
    		int x=getint(),y=getint(),z=getint();
    		addedge(x,y,z);addedge(y,x,z);
    	}
    	mx[0]=n+1;totsize=n;root=0;
    	getroot(1,0);rt=root;
    	Build(root);
    	int l=0,r=5e8;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(Judge(mid))l=mid+1;
    		else r=mid-1;
    	}
    	memset(vis,0,sizeof(vis));
    	lim=l;ans=0;solve2(rt);
    	while(Ans.size()<m)Ans.push_back(l-1);
    	sort(Ans.begin(),Ans.end(),greater<int>());
    	for(int i=0;i<m;i++)cout<<Ans[i]<<"
    ";
    }
    
  • 相关阅读:
    【深入理解JAVA虚拟机】第一部分.走进Java
    【设计模式最终总结】桥接模式 VS 外观模式
    【设计模式最终总结】组合模式:树形结构的处理
    【设计模式最终总结】桥接模式(接口模式):处理多维度变化
    【设计模式最终总结】适配器模式
    深入编解码:ASCII,UNICODE,UTF8等
    好妈妈【第四章】培养良好的学习习惯
    【设计模式最终总结】建造者模式:复杂对象的组装与创建
    上火流鼻血
    Spring Data Jpa 学习笔记
  • 原文地址:https://www.cnblogs.com/Trrui/p/9686158.html
Copyright © 2020-2023  润新知