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);
}
}