【bzoj4151】 [AMPPZ2014]The Cave

不错的题,想了挺久的。

不妨把点1设为根节点。
对于每个限制,到达 aibi 这条路径的距离一定不大于某个数,在根节点确定的情况下,第i个限制的距离最大就是 max{0,dist[ai]+dist[bi]di2} 以内,其中dist表示该点到根节点的距离。
这就可以转化成类似一堆已知某个端点的线段求某个交点的问题。
于是先取限制距离最大的线段上的点,然后一路往上跑直到某个点刚好满足限制,对于这个点再判断一下是否满足所有情况即可,总复杂度 O(n+m)

参考了一下zimpha的写法。

看了下AMPPZ的官方solution似乎有 O(n+mlogn) 的做法,然而并不能看懂(波兰语)2333。

然而不知道为什么跑得超级超级超级慢QAQ
(唔memset太多次了?)

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define fore(i,u) for(int i=head[u];i;i=nxt[i])
#define cr(x) memset(x , 0 , sizeof x)
#define maxn 300007
#define maxm 600007

inline int rd() {
    char c = getchar();
    while (!isdigit(c)) c = getchar() ; int x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x;
}

typedef int arr[maxn];
typedef int adj[maxm];

arr head , dis , fa , a , b , d;
adj to , nxt;
int n , m , ett;

inline void add(int u , int v) {
    to[++ ett] = v , nxt[ett] = head[u] , head[u] = ett;
}

inline void ins(int u , int v) {
    add(u , v) , add(v , u);
}

void input() {
    n = rd() , m = rd();
    ett = 0 , cr(head);
    rep(i , 2 , n) ins(rd() , rd());
    rep(i , 1 , m) a[i] = rd() , b[i] = rd() , d[i] = rd();
}

void dfs(int u) {
    fore(i , u) {
        int v = to[i];
        if (v == fa[u]) continue;
        dis[v] = dis[u] + 1 , fa[v] = u , dfs(v);
    }
}

void solve() {
    int u = 1;
    rep(times , 1 , 2) {
        dis[u] = 0 ; cr(fa) ; dfs(u);
        int rec_d = 0 , rec_u = 0;
        rep(i , 1 , m) {
            int t = (dis[a[i]] + dis[b[i]] - d[i] + 1) / 2;
            if (rec_d < t) rec_d = t , rec_u = a[i];
        }
        if (!rec_d) {
            printf("TAK\n%d\n" , u);
            return ;
        }
        u = rec_u;
        rep(i , 1 , dis[rec_u] - rec_d) u = fa[u];
    }
    puts("NIE");
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("data.txt" , "r" , stdin);
    #endif
    per(T , rd() , 1) {
        input();
        solve();
    }
    return 0;
}

你可能感兴趣的:(【bzoj4151】 [AMPPZ2014]The Cave)