[IOI2011]Race

题意

Here

思考

简要题意:给一棵树,每条边有权。求一条简单路径,权值和等于 \(K\),且边的数量最小。

由于这条最小路径可以是所有路径中的任意一个,所以所有等于 \(K\) 的路径我们必须考虑到,关于树上的路径统计问题,我们选用点分治。

这样一想就是点分治裸题了,由于 \(K \leq 1e6\),我们可以开个桶然后套路计算了,对于每颗子树计算完后再加入答案,避免重复计算(大于 \(K\) 的路径也可以剪点枝)。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 200020;
const int M = 1000010;
const int oo = 0x3f3f3f3f;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x * f;
}
struct node{
    int nxt, to, dis;
}edge[N << 1];
int head[N], num;
void build(int from, int to, int dis){
    edge[++num].nxt = head[from];
    edge[num].to = to;
    edge[num].dis = dis;
    head[from] = num;
}
int root, ANS, n, k, sum, vis[N], f[N], sz[N], dist[N], ans[M];
void getroot(int u, int fa){
    sz[u] = 1; f[u] = 0;
    for(int i=head[u]; i; i=edge[i].nxt){
        int v = edge[i].to;
        if(v == fa || vis[v]) continue;
        getroot(v, u);
        sz[u] += sz[v];
        f[u] = max(f[u], sz[v]);
    }
    f[u] = max(f[u], sum - sz[u]);
    if(f[root] > f[u]) root = u;
}
void dfs(int u, int fa, int cnt){
    if(dist[u] > k) return;
    ANS = min(ANS, ans[ k - dist[u] ] + cnt);
    for(int i=head[u]; i; i=edge[i].nxt){
        int v = edge[i].to;
        if(v == fa || vis[v]) continue;
        dist[v] = dist[u] + edge[i].dis;
        dfs(v, u, cnt + 1);
    }
}
void add(int u, int fa, int cnt){
    if(dist[u] > k) return;
    ans[ dist[u] ] = min(ans[ dist[u] ], cnt);
    for(int i=head[u]; i; i=edge[i].nxt){
        int v = edge[i].to;
        if(v == fa || vis[v]) continue;
        add(v, u, cnt + 1);
    }
}
void clearx(int u, int fa, int dist){
    if(dist > k) return;
    ans[ dist ] = oo;
    for(int i=head[u]; i; i=edge[i].nxt){
        int v = edge[i].to;
        if(v == fa || vis[v]) continue;
        clearx(v, u, edge[i].dis + dist);
    }
}
void solve(int u){
    vis[u] = 1; ans[0] = 0;
    for(int i=head[u]; i; i=edge[i].nxt){
        int v = edge[i].to;
        if(vis[v]) continue;
        dist[v] = edge[i].dis;
        dfs(v, u, 1);
        add(v, u, 1);
    }
    clearx(u, 0, 0);
    for(int i=head[u]; i; i=edge[i].nxt){
        int v = edge[i].to;
        if(vis[v]) continue;
        root = 0;
        sum = sz[v];
        getroot(v, u);
        solve(root);
    }
}
int main(){
    f[0] = ANS = oo;
    memset(ans, 0x3f, sizeof(ans));
    n = read(), k = read();
    for(int i=1; i<=n-1; i++){
        int u, v, d;
        u = read(), v = read(), d = read();
        build(u + 1, v + 1, d);
        build(v + 1, u + 1, d);
    }
    sum = n;
    root = 0;
    getroot(1, 0);
    solve(root);
    if(ANS != oo) cout << ANS;
    else puts("-1");
    return 0;
}

总结

写完之后 \(T\)\(n\) 回,发现是自己找重心的时候 \(f[]\) 数组没清零,导致这个复杂度啊,有点点大。以后要记得重置数组,不然都没法查错啊这个。

posted @ 2018-11-26 22:40  alecli  阅读(100)  评论(0编辑  收藏  举报