HDU - 4871Shortest-path tree(最小路径生成树+点分治)

HDU - 4871Shortest-path tree(最小路径生成树+点分治)

题意:
给定n个点,m条边。要求在图中找到一棵树,使得所有点到1的距离最短,树的字典序最小,然后给定k个点,要求经过k个点的路程最长,并求出最长的有几条

思路:

第一问:求以1为根节点的最短路径树
第二问:假设当前以x为根节点,枚举其已经遍历过的节点深度+刚遍历的节点深度==k-1(k个节点,k-1条边)。对已经遍历过的节点深度及时更新最大值并计数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int inf=0x3f3f3f3f;
const int maxn=2e5+5;
int t,n,m,k1,k,rt,cnt,ans,len;
int head[maxn],to[maxn],nex[maxn],w[maxn];
int maxp[maxn],siz[maxn],vis[maxn],d[maxn];
int pre[maxn],pw[maxn],tmp[maxn],dis[maxn];
int pdep[maxn],tdep[maxn],dep[maxn],pdis[maxn];
struct node{
	int u,id;
	bool operator < (const node &x)const{
		return u>x.u;
	}
};
priority_queue<node>p;
void add(int x,int y,int z){
	nex[++k1]=head[x];
	to[k1]=y;
	w[k1]=z;
	head[x]=k1;
}
void dij(){
	memset(d,inf,sizeof(d));
	memset(vis,0,sizeof(vis));
	d[1]=0;
	p.push((node){0,1});
	while(!p.empty()){
		int x=p.top().id;
		p.pop();
		if(vis[x])continue;
		vis[x]=1;
		for(int i=head[x];i;i=nex[i]){
			int y=to[i];
			if(d[y]>d[x]+w[i]||d[y]==d[x]+w[i]&&pre[y]>x){
				d[y]=d[x]+w[i];
				pre[y]=x;
				pw[y]=w[i];
				p.push((node){d[y],y});
			}
		}
	}
	memset(head,0,sizeof(head));
	for(int i=2;i<=n;i++){
		add(i,pre[i],pw[i]),add(pre[i],i,pw[i]);
	}
}
void getzx(int x,int f){//dp求重心 
	siz[x]=1,maxp[x]=0;
	for(int i=head[x];i;i=nex[i]){
		int y=to[i];
		if(y==f||vis[y])continue;
		getzx(y,x);
		siz[x]+=siz[y];
		maxp[x]=max(maxp[x],siz[y]);
	}
	maxp[x]=max(maxp[x],cnt-siz[x]);
	if(maxp[x]<maxp[rt])rt=x;
}
void getdis(int x,int f){
	if(dep[x]>k)return;
	tmp[++tmp[0]]=dis[x];
	tdep[tmp[0]]=dep[x];
	for(int i=head[x];i;i=nex[i]){
		int y=to[i];
		if(vis[y]||y==f)continue;
		dis[y]=dis[x]+w[i];
		dep[y]=dep[x]+1;
		getdis(y,x);
	}
}
void calc(int x){
	int md=0;
	for(int i=head[x];i;i=nex[i]){
		int y=to[i];
		if(vis[y])continue;
		tmp[0]=0;
		dep[y]=1;
		pdep[0]=1;
		dis[y]=w[i];
		getdis(y,x);
		for(int j=1;j<=tmp[0];j++){
			if(k>=tdep[j]){
				if(pdis[k-tdep[j]]+tmp[j]>ans)ans=pdis[k-tdep[j]]+tmp[j],len=pdep[k-tdep[j]];
				else if(pdis[k-tdep[j]]+tmp[j]==ans)len+=pdep[k-tdep[j]];
			}
		}
		for(int j=1;j<=tmp[0];j++){
			if(k>=tdep[j]){
				if(pdis[tdep[j]]<tmp[j])pdis[tdep[j]]=tmp[j],pdep[tdep[j]]=1;
				else if(pdis[tdep[j]]==tmp[j])pdep[tdep[j]]++;
				md=max(md,tdep[j]);
			}
		}
	}
	for(int i=1;i<=md;i++)pdis[i]=pdep[i]=0;
}
void solve(int x){
	vis[x]=1;
	calc(x);
	for(int i=head[x];i;i=nex[i]){
		int y=to[i];
		if(vis[y])continue;
		cnt=siz[y],maxp[rt=0]=inf;//初始化 
		getzx(y,0);//找子树重心 
		solve(rt);//对重心 
	}
}
int main(){
	scanf("%d",&t);
	while(t--){
		ans=k1=0;
		memset(head,0,sizeof(head));
		scanf("%d%d%d",&n,&m,&k);
		k--;
	    for(int i=1,x,y,z;i<=m;i++){
		    scanf("%d%d%d",&x,&y,&z);
		    add(x,y,z),add(y,x,z);
	    }
	    dij();
	    memset(vis,0,sizeof(vis));
	    cnt=n;
	    maxp[rt=0]=inf;
	    getzx(1,0);
	    solve(rt);
	    printf("%d %d\n",ans,len);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值