zoukankan      html  css  js  c++  java
  • [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[]) 数组没清零,导致这个复杂度啊,有点点大。以后要记得重置数组,不然都没法查错啊这个。

  • 相关阅读:
    this指向问题
    b继承a的函数
    如何解决跨域问题
    事件冒泡和阻止事件冒泡
    Spring5(二)——IOC
    MySQL基础(四)——
    MySQL基础(二)——常用命令
    MySQL基础(一)——入门
    Linux(二)——常用命令
    Linux(一)——简介
  • 原文地址:https://www.cnblogs.com/alecli/p/10023552.html
Copyright © 2011-2022 走看看