Luo Gu P2680 transportation plan - a tree difference edge

Title connection: https: //www.luogu.com.cn/problem/P2680
Title effect:
Here Insert Picture Description
Here Insert Picture Description
Here Insert Picture Description
Here Insert Picture Description
if X to complete, then Y> X time must be completed. Monotone, half time.

For a time mid. K is larger than the mid paths must be removed after one side has become <= mid. So this edge through which a k paths. This difference is determined by the tree edge. After this edge k paths found in the largest one ans at all. If the first k paths -ans <= mid. L = mid + 1.
Otherwise: R = mid-1.

#include <bits/stdc++.h>
#define LL long long
using namespace std;

struct node{
    int to, c;
};
vector<node> v[300005];
//深度        //倍增                      //s: 和根节点的距离 c:差分数组 fs:第i个节点代表的那条边
int d[300005], f[300005][25], lg[300005], s[300005], c[300005], fs[300005];
int tot;

struct eg{
    int u, to, s;
};

vector<eg> e;

void dfs(int u, int fa){

    d[u]=d[fa]+1;
    f[u][0]=fa;
    for(int i=1; (1<<i)<=d[u]; i++){
        f[u][i]=f[f[u][i-1]][i-1];
    }
    for(int i=0; i<v[u].size(); i++){
        if(v[u][i].to!=fa){
            s[v[u][i].to]=s[u]+v[u][i].c;
            dfs(v[u][i].to, u);
            fs[v[u][i].to]=v[u][i].c;
        }
    }
}

int LCA(int x ,int y){
    if(d[x]<d[y]){
        swap(x, y);
    }
    while(d[x]!=d[y]){
        x=f[x][lg[d[x]-d[y]]-1];
    }
    if(x==y){
        return x;
    }
    for(int k=lg[d[x]]; k>=0; k--){
        if(f[x][k]!=f[y][k]){
            x=f[x][k], y=f[y][k];
        }
    }
    return f[x][0];
}

int cmp(eg a, eg b){
    return a.s>b.s;
}

int ans=0;

void get_max(int u, int fa, int k){//得到是前k条路径都经过的边的最大值
    for(int i=0; i<v[u].size(); i++){
        int to=v[u][i].to;
        if(to!=fa){
            get_max(to, u, k);
            c[u]+=c[to];
            c[to]=0;//初始化回去
        }
    }
    if(c[u]==k){
        ans=max(fs[u], ans);
    }
}

int slove(int mid, int n){
    int k=e.size();
    for(int i=0; i<e.size(); i++){//找到比mid大的k条边
        if(e[i].s<=mid){
            k=i;
            break;
        }
    }
    for(int i=0; i<k; i++){//树上边差分
        int a=e[i].u, b=e[i].to;
        c[a]++, c[b]++, c[LCA(a, b)]-=2;
    }
    ans=0;
    get_max(1, 0, k);//找到经过这k条路径边的最大值
    for(int i=0; i<k; i++){
        if(e[i].s-ans>mid){
            return 0;
        }
    }
    return 1;
}

int main(){

    int n, m, a, b, c;
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++) {//预处理
        lg[i]=lg[i-1];
        if(i==1<<lg[i-1])lg[i]++;
    }

    for(int i=1; i<n; i++){
        scanf("%d%d%d", &a, &b, &c);
        v[a].push_back(node{b, c});
        v[b].push_back(node{a, c});
    }
    s[1]=0;
    dfs(1,0);//从树根开始dfs
    for(int i=1; i<=m; i++){
        scanf("%d%d", &a, &b);
        e.push_back(eg{a, b, s[a]+s[b]-2*s[LCA(a, b)]});
    }
    sort(e.begin(), e.end(), cmp);
    
    //L:边的最大值为1000, 所以路径最多减小1000
    //还有,不加这个优化会T掉一个点(各种 卡常+优化 都过不去
    int R=e[0].s, L=max(0, R-1000), k;
    
    while(L<=R){
        int mid=(L+R)/2;
        if(slove(mid, n)){
            R=mid-1; k=mid;
        }
        else{
            L=mid+1;
        }
    }
    printf("%d\n", k);

    return 0;
}
Published 374 original articles · won praise 22 · views 20000 +

Guess you like

Origin blog.csdn.net/qq_21433411/article/details/103970904