1. 程式人生 > >[IOI2011]Race

[IOI2011]Race

get 統計 等於 sizeof name 自己 href ret 套路

題意

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[]\) 數組沒清零,導致這個復雜度啊,有點點大。以後要記得重置數組,不然都沒法查錯啊這個。

[IOI2011]Race