bzoj2599: [IOI2011]Race

题目链接

bzoj2599: [IOI2011]Race

题解

点分治,用t[k]表示子树中距离root为k 的最小边路径
转移时先与前边子树和合并更新答案,然后更新距离父节点最优值,这样就保证不在同一子树内了
每次分层的时候记得清除答案

代码

#include<cstdio> 
#include<cstring> 
#include<algorithm> 
inline int read() { 
    int x = 0,f = 1; 
    char c = getchar(); 
    while(c < '0' || c > '9'){if(c == '-')f = -1;c = getchar(); } 
    while(c <= '9' && c >= '0')x = x * 10 + c - '0',c = getchar(); 
    return x * f; 
} 
#define INF 1000000007
const int maxn = 200007;
struct node {
    int v,w,next;
} edge[maxn << 1]; 
int head[maxn],num;
inline void add_edge(int u,int v,int w) { 
    edge[++ num].v = v;edge[num].w = w;edge[num].next = head[u];head[u] = num;
} 
int n,k,root; 
bool vis[maxn]; 
int dis[maxn],son[maxn],f[maxn],deep[maxn],t[1000007],tot; 
void get_root(int x,int fa) { 
    son[x] = 1;f[x] = 0; 
    for(int i = head[x];i;i = edge[i].next) { 
        int v = edge[i].v; 
        if(v == fa || vis[v]) continue;     
        get_root(v,x); 
        son[x] += son[v]; f[x] = std::max(f[x],son[v]); 
    } 
    f[x] = std::max(tot - son[x],f[x]); 
    if(f[x] < f[root])root = x; 
} 
int ans = INF; 
void calc(int x,int fa) { 
    if(dis[x] <= k) ans = std::min(ans,deep[x] + t[k - dis[x]]); 
    else return;
    for(int i = head[x];i;i = edge[i].next) { 
        int v = edge[i].v; 
        if(vis[v] || edge[i].v == fa) continue; 
        deep[v] = deep[x] + 1; 
        dis[v] = dis[x] + edge[i].w; 
        calc(v,x); 
    }   
} 
void update(int x,int fa,bool ty) { 
    if(dis[x] <= k) { 
        if(ty)t[dis[x]] = std::min(t[dis[x]],deep[x]); 
        else t[dis[x]] = n; 
    } 
    for(int i = head[x];i;i = edge[i].next) { 
        int v = edge[i].v; 
        if(v != fa && !vis[v]) update(v,x,ty); 
    } 
} 
void sol(int x) { 
    vis[x] = 1; t[0] = 0; 
    for(int i = head[x];i;i = edge[i].next) { 
        int v = edge[i].v;
        if(vis[v]) continue; 
        deep[v] = 1;dis[v] = edge[i].w; 
        calc(v,0);update(v,0,1); 
    } 
    for(int i = head[x];i;i = edge[i].next) 
        if(!vis[edge[i].v]) update(edge[i].v,0,0);
    for(int i = head[x];i;i = edge[i].next) {  
            if(!vis[edge[i].v]) { 
            root = 0; 
            tot = son[edge[i].v]; 
            get_root(edge[i].v,0); sol(root); 
        }       
    } 
} 
int main() { 
    n = read(),k = read(); 
    for(int i = 1;i <= k;++ i) t[i] = n; 
    for(int u,v,w,i = 1;i < n;++ i) { 
        u = read() + 1,v = read() + 1,w = read(); 
        add_edge(u,v,w); add_edge(v,u,w); 
    }   
    ans = tot = n;f[0] = n; 
    get_root(1,0); 
    sol(root); 
    printf("%d\n",ans == n ? -1 : ans); 
    return 0; 
} 

猜你喜欢

转载自www.cnblogs.com/sssy/p/9249348.html