1. 程式人生 > >HDU4871 Shortest-path tree(點分治)

HDU4871 Shortest-path tree(點分治)

clr scan 路徑 spa 最長路 res 復雜度 mem for

題目

題面

大意是給你一個圖,要你構建出一棵最短路樹,再詢問經過k個點的最長路徑長度以及最長路徑條數。

思路

點分治。
點分治的思路是這樣的:
對於一個點\(x\)而言,對答案有影響的路徑要麽經過點\(x\)要麽不經過,利用這點進行分治。
點分治首先要找出一個重心。重心是指以該點為根所有的子樹中sz最大的最小。
然後,對於每一棵子樹進行處理,統計答案(點分治題目不同的地方就在這,其它都是板子)
之後再遞歸進重心的子樹中處理。

對於一個根來說,如果統計答案的復雜度是\(O(n)\),那麽總的復雜度就是\(O(nlogn)\).

代碼

#include<bits/stdc++.h>
#define M 30005
#define clr(x,y) memset(x,y,sizeof(x))
using namespace std;
int T,n,m,K;
int g[M],tt;
int ansa=0,ansc=0;//最長路,最長路的種數 
struct Node{
    int to,co;
    bool operator < (const Node& res) const{
        return to<res.to;   
    }
};
vector<Node>G[M]; 
struct edge{
    int nxt,to,co;  
}H[M<<1];
void add(int a,int b,int c){
    H[++tt]=(edge){g[a],b,c};
    g[a]=tt;
}
struct node{
    int x,ds;
    bool operator < (const node& res) const{
        return ds>res.ds;   
    }
};
priority_queue<node>Q;
int dis[M];
bool vis[M];
void Dij(){
    clr(dis,0x3f);clr(vis,0);
    dis[1]=0;
    Q.push((node){1,0});
    while(!Q.empty()){
        node e=Q.top();Q.pop();
        if(vis[e.x])continue;vis[e.x]=1;
        for(int i=0;i<(int)G[e.x].size();i++){
            int u=G[e.x][i].to,v=G[e.x][i].co;
            if(dis[u]>dis[e.x]+v){
                dis[u]=dis[e.x]+v;
                Q.push((node){u,dis[u]});   
            }
        }
    }
}
bool solved[M];
int sz[M],ttot;
void dfs(int x,int f,int ds){//構造樹 
    for(int i=0;i<(int)G[x].size();i++){
        int u=G[x][i].to,v=G[x][i].co;
        if(u==f||vis[u]||ds+v>dis[u])continue;
        vis[u]=1;
        add(u,x,v);add(x,u,v);
        dfs(u,x,ds+v);
    }
}
int mi=1e9,zx=1;
void dfs_zx(int x,int f){//尋找重心 
    sz[x]=1;int mm=0;
    for(int i=g[x];i;i=H[i].nxt){
        int u=H[i].to;
        if(u==f||solved[u])continue;
        dfs_zx(u,x);
        sz[x]+=sz[u];
        mm=max(mm,sz[u]);
    }
    mm=max(mm,ttot-sz[x]);
    if(mm<mi)mi=mm,zx=x;
}
void find_zx(int rt){
    mi=1e9,zx=1;
    dfs_zx(rt,0);
}
void getans(int x,int y){
    if(x>ansa)ansa=x,ansc=y;
    else if(x==ansa)ansc+=y;
}
int mx[M],mn[M];
int num[M],siz=0;
void dfs_ans(int x,int f,int ds,int c){
    dis[++siz]=ds;num[siz]=c;
    for(int i=g[x];i;i=H[i].nxt){
        int u=H[i].to,v=H[i].co;
        if(solved[u]||u==f)continue;
        dfs_ans(u,x,ds+v,c+1);  
    }
}
void solve(int rt){
    find_zx(rt);
    solved[zx]=1;
    for(int i=0;i<=K;i++)mx[i]=mn[i]=0;mn[0]=1;
    for(int i=g[zx];i;i=H[i].nxt){
        int u=H[i].to,v=H[i].co;
        if(solved[u])continue;
        siz=0;
        dfs_ans(u,zx,v,1); 
        for(int j=1;j<=siz;j++){
            if(num[j]>=K)continue;
            int tmp=K-1-num[j];
            getans(mx[tmp]+dis[j],mn[tmp]);
        }
        for(int j=1;j<=siz;j++){
            if(num[j]>=K)continue;
            if(dis[j]>mx[num[j]]){
                mx[num[j]]=dis[j];
                mn[num[j]]=1;
            }
            else if(dis[j]==mx[num[j]])mn[num[j]]++;
        }
    }
    ttot--;
    for(int i=g[zx];i;i=H[i].nxt){
        int u=H[i].to;
        if(solved[u])continue;
        solve(u);
    }
}
int main(){
    cin>>T;
    while(T--){
        clr(g,0);clr(solved,0);tt=ansa=ansc=0;
        scanf("%d%d%d",&n,&m,&K);ttot=n;
        for(int i=1;i<=n;i++)G[i].clear(); 
        for(int i=1,a,b,c;i<=m;i++){
            scanf("%d%d%d",&a,&b,&c);
            G[a].push_back((Node){b,c});
            G[b].push_back((Node){a,c});
        }
        for(int i=1;i<=n;i++)sort(G[i].begin(),G[i].end()); 
        Dij();
        clr(vis,0);dfs(1,0,0);
        solve(1);
        printf("%d %d\n",ansa,ansc);
    }
    return 0;
}

HDU4871 Shortest-path tree(點分治)