洛谷 2680 运输计划

来源:互联网 发布:2017通货膨胀 知乎 编辑:程序博客网 时间:2024/06/04 23:52
//二分+倍增LCA+树上前缀和//尽力缩小二分范围,能多拿分就多拿分//掌握树上前缀和的初始化和区间修改 //读入优化//二分答案,然后找出超出mid的计划,记录他们的公共重边,这里用树上前缀和找前缀和exc==超出计划数 的最大的边,删掉后再判断是否<=mid。 #include<bits/stdc++.h>using namespace std;const int inf = 0x3f3f3f3f;const int maxn = 300010;int n, m, tot, maxw, maxl;int st[maxn*2], dep[maxn], f[maxn][23], dis[maxn], from[maxn], lca[maxn], s[maxn], e[maxn], t[maxn];int len[maxn];struct node{    int v, w, nxt;} edge[2*maxn];inline int read(){    int num = 0;    char c;    while((c = getchar()) == ' ' || c == '\n' || c == '\r');    num = c - '0';    while(isdigit(c = getchar()))   num = num*10 + c - '0';    return num;}inline void in(int x, int y, int z){    edge[++tot].v = y;    edge[tot].w = z;    edge[tot].nxt = st[x];    st[x] = tot;}///inline void BuildT(int now){    dep[now] = dep[f[now][0]] + 1;    for(int i = 1; i <= 21; i++){        if(dep[now] < (1<<i))   break;//剪枝         f[now][i] = f[f[now][i-1]][i-1];    }    for(int i = st[now]; i; i = edge[i].nxt){        int to = edge[i].v;        if(to != f[now][0]){            f[to][0] = now;            dis[to] = dis[now] + edge[i].w;            from[to] = i;            BuildT(to);         }    }}///inline int LCA(int x, int y){    if(dep[x] < dep[y]) swap(x, y);    int delta = dep[x] - dep[y];    for(int i = 0; i <= 21; i++)        if(delta & (1<<i))  x = f[x][i];//注意这个条件     if(x == y)  return x;    for(int i = 21; i >= 0; i--)        if(f[x][i] != f[y][i]){            x = f[x][i];            y = f[y][i];        }    return f[x][0];}///inline int getLen(int num, int p, int q){    return dis[p] + dis[q] - (dis[lca[num] = LCA(p, q)] << 1);}///inline void change(int num, int turn){//区间修改树上前缀和     t[s[num]] += turn;    t[e[num]] += turn;    t[lca[num]] -= (turn << 1);}///inline int getMax(int now, int cnt){    int exc = t[now];//前缀和累加器,记录有多少计划经过这条边     for(int i = st[now]; i; i = edge[i].nxt){        int to = edge[i].v;        if(to == f[now][0]) continue;        exc += getMax(to, cnt);    }    if(exc == cnt)  maxw = max(maxw, edge[from[now]].w);//找到公共重边     return exc;}inline bool check(int now){    memset(t, 0, sizeof(t));    int cnt = 0;    for(int i = 1; i <= m; i++)        if(len[i] > now){            cnt++;            change(i, 1);        }    if(!cnt)    return 1;    maxw = 0;    getMax(1, cnt);    /*for(int i = 1; i <= n; i++)        printf("%d ", t[i]);*/    //printf("%d\n", maxw);    return maxl - maxw <= now;}int main(){    int maxz = 0;    n = read();    m = read();    for(int i = 1, x, y, z; i < n; i++){        x = read();        y = read();        z = read();        maxz = max(maxz, z);        in(x, y, z);        in(y, x, z);    }    f[1][0] = 0;    BuildT(1);    maxl = 0;    for(int i = 1; i <= m; i++){        s[i] = read();        e[i] = read();        len[i] = getLen(i, s[i], e[i]);        maxl = max(maxl, len[i]);    }    int l = maxl - maxz, r = maxl + 1, mid;    while(l < r){        mid = l+r >> 1;        if(check(mid))  r = mid;        else l = mid + 1;//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!    }    /*for(int i = 1; i <= m; i++)        printf("%d ", lca[i]);*/    printf("%d\n", l);    return 0;}