[Luogu] 运输计划--000

https://www.luogu.org/problemnew/show/P2680

题解:

二分一个答案x之后,只需要考虑m条路径中路径长度大于x的那些路径,并对那些路径求一个交。设m中最长路径为l,则只需判断路径交中的边是否存在一条边e使得e.w>=l-x。如何求交?其实我们树链剖分之后,只需用一个差分数组来维护每条边被多少条路径覆盖,只需考虑被覆盖的路径条数等于当前考虑的路径条数的边即可
复杂度分析:差分数组的复杂度仅为O(1),总时间复杂度为O(nlognlogw),w<=3e8。由于差分数组和树链剖分的常数都较小,因此可以通过该题

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define clr(a,b) memset(a,b,sizeof(a))
#define maxn 300000
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
using namespace std;
int n,m;
inline void swap(int &a,int &b) {
    int t=a;
    a=b;
    b=t;
}
struct EDGE {
    int u,v,w,next;
} edge[maxn*2+10];
int head[maxn+10],pp;
void adde(int u,int v,int w) {
    edge[++pp]=(EDGE) {
        u,v,w,head[u]
    };
    head[u]=pp;
}
int de[maxn+10],sz[maxn+10],fa[maxn+10],hs[maxn+10],w[maxn+10];
int que[maxn+10],he,ta;
vector<int>ch[maxn+10];
int top[maxn+10],id[maxn+10],s[maxn+10],tot;
void bfs() {
    que[he=ta=1]=1;
    while(he<=ta) {
        int u=que[he++];
        for(int i=head[u]; i; i=edge[i].next) {
            int v=edge[i].v;
            if(fa[u]!=v) {
                w[v]=edge[i].w;
                que[++ta]=v;
                fa[v]=u;
                de[v]=de[u]+1;
            }
        }
    }
    for(int j=ta; j; j--) {
        int u=que[j];
        sz[u]=1;
        for(int i=head[u]; i; i=edge[i].next) {
            int v=edge[i].v;
            if(fa[u]!=v) {
                sz[u]+=sz[v];
                if(sz[v]>sz[hs[u]])hs[u]=v;
            }
        }
    }
    top[1]=1;
    ch[1].push_back(1);
    for(int j=1; j<=ta; j++) {
        int u=que[j];
        for(int i=head[u]; i; i=edge[i].next) {
            int v=edge[i].v;
            if(fa[u]!=v) {
                if(v==hs[u])top[v]=top[u];
                else top[v]=v;
                ch[top[v]].push_back(v);
            }
        }
    }
    for(int u=1; u<=n; u++) {
        for(int i=0; i<ch[u].size(); i++) {
            int v=ch[u][i];
            id[v]=++tot;
            s[tot]=s[tot-1]+w[v];
        }
    }
}
void dfs1(int u) {
    sz[u]=1;
    for(int i=head[u]; i; i=edge[i].next) {
        int v=edge[i].v;
        if(v!=fa[u]) {
            fa[v]=u;
            de[v]=de[u]+1;
            w[v]=edge[i].w;
            dfs1(v);
            sz[u]+=sz[v];
            if(sz[v]>sz[hs[u]])hs[u]=v;
        }
    }
}
void dfs2(int u) {
    id[u]=++tot;
    s[tot]=s[tot-1]+w[u];
    if(hs[u]) {
        top[hs[u]]=top[u];
        dfs2(hs[u]);
    }
    for(int i=head[u]; i; i=edge[i].next) {
        int v=edge[i].v;
        if(fa[u]!=v&&v!=hs[u]) {
            top[v]=v;
            dfs2(v);
        }
    }
}
int u1[maxn+10],u2[maxn+10],d[maxn+10];
int sum(int l,int r) {
    return s[r]-s[l-1];
}
int dis(int u,int v) {
    if(u==v)return 0;
    if(top[u]==top[v]) {
        if(de[u]>de[v])swap(u,v);
        return sum(id[hs[u]],id[v]);
    }
    if(de[top[u]]>de[top[v]])swap(u,v);
    return sum(id[top[v]],id[v])+dis(u,fa[top[v]]);
}
//b为差分数组
int b[maxn+10];
void update(int l,int r) {
    b[l]++;
    b[r+1]--;
}
void modify(int u,int v) {
    if(top[v]==top[u]) {
        if(de[u]>de[v])swap(u,v);
        update(id[hs[u]],id[v]);
    } else {
        if(de[top[u]]>de[top[v]])swap(u,v);
        update(id[top[v]],id[v]);
        modify(u,fa[top[v]]);
    }
}
bool judge(int x) {
    clr(b,0);
    int cnt=0,m1=0;
    for(int i=1; i<=m; i++)if(d[i]>x) {
            cnt++;
            m1=max(m1,d[i]-x);
            modify(u1[i],u2[i]);
        }
    int tot=0,m2=0;
    for(int i=1; i<=n; i++) {
        tot+=b[i];
        if(tot==cnt)m2=max(m2,sum(i,i));
    }
    return m2>=m1;
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1; i<n; i++) {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        adde(a,b,c);
        adde(b,a,c);
    }
    bfs();
    int l=0,r=0;
    for(int i=1; i<=m; i++) {
        scanf("%d%d",&u1[i],&u2[i]);
        d[i]=dis(u1[i],u2[i]);
        r=max(r,d[i]);
    }
    int ans;
    while(l<=r) {
        int mid=(l+r)/2;
        if(judge(mid)) {
            r=mid-1;
            ans=mid;
        } else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

GG--未实现

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>

using namespace std;
const int N = 3e5 + 10;

#define gc getchar()
#define lson jd << 1
#define rson jd << 1 | 1 

int head[N], deep[N], fa[N], topp[N], son[N], tree[N], siz[N], data[N], bef[N];
struct Node {int v, w, nxt;} G[N << 1];
struct Node_2 {int l, r, dis, Max, Maxfrom;} Ask[N];
struct Node_3 {int l, r, w, Max, Maxfrom;} T[N << 2];
int n, m, now, tim, ans;

inline int read(){
    int x = 0; char c = gc;
    while(c < ‘0‘ || c > ‘9‘) c = gc;
    while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = gc;
    return x;
}

inline void add(int u, int v, int w){
    G[now].v = v; G[now].w = w; G[now].nxt = head[u]; head[u] = now ++;
}

void dfs_find_son(int u, int f_, int dep){
    fa[u] = f_;
    deep[u] = dep;
    siz[u] = 1;
    for(int i = head[u]; ~ i; i = G[i].nxt){
        int v = G[i].v;
        if(v != f_){
            data[v] = G[i].w;
            dfs_find_son(v, u, dep + 1);
            siz[u] += siz[v];
            if(siz[v] > siz[son[u]]) son[u] = v;
        }
    }
}

void dfs_to_un(int u, int tp){
    topp[u] = tp;
    tree[u] = ++ tim;
    bef[tim] = u;
    if(!son[u]) return ;
    dfs_to_un(son[u], tp);
    for(int i = head[u]; ~ i; i = G[i].nxt){
        int v = G[i].v;
        if(v != fa[u] && v != son[u]) dfs_to_un(v, v);
    }
}

void updata(int jd) {
    T[jd].w = T[lson].w + T[rson].w;
    T[jd].Max = max(T[lson].Max, T[rson].Max);
}

void build_tree(int l, int r, int jd){
    T[jd].l = l; T[jd].r = r;
    if(l == r) {
        T[jd].w = data[bef[l]];
        T[jd].Max = T[jd].w;
        return ;
    }
    int mid = (l + r) >> 1;
    build_tree(l, mid, lson);
    build_tree(mid + 1, r, rson);
    updata(jd);
}

void Sec_A(int l, int r, int jd, int x, int y){
    if(x <= l && r <= y) {ans += T[jd].w; return ;}
    int mid = (l + r) >> 1;
    if(x <= mid) Sec_A(l, mid, lson, x, y);
    if(y > mid)  Sec_A(mid + 1, r, rson, x, y);
}

int Sec_A_imp(int x, int y){
    int tp1 = topp[x], tp2 = topp[y], answer = 0;
    while(tp1 != tp2){
        if(deep[tp1] < deep[tp2]) swap(x, y), swap(tp1, tp2);
        ans = 0;
        Sec_A(1, n, 1, tree[tp1], tree[x]);
        answer += ans;
        x = fa[tp1];
        tp1 = deep[x];
    }
    if(x == y) return answer;
    ans = 0;
    if(deep[x] < deep[y]) swap(x, y);
    Sec_A(1, n, 1, tree[y] + 1, tree[x]);
    answer += ans;
    return answer;
} 

void Poi_A(int l, int r, int jd, int x, int y){
    if(x <= l && r <= y) {
        if(T[jd].Max > ans) {ans = T[jd].Max;}
        return ;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) Poi_A(l, mid, lson, x, y);
    if(y > mid)  Poi_A(mid + 1, r, rson, x, y);
}

int Poi_A_imp(int x, int y){
    int tp1 = topp[x], tp2 = topp[y], answer = 0;
    while(tp1 != tp2){
        if(deep[tp1] < deep[tp2]) swap(x, y), swap(tp1, tp2);
        ans = 0;
        Poi_A(1, n, 1, tree[tp1], tree[x]);
        if(ans > answer) {answer = ans;}
        x = fa[tp1];
        tp1 = topp[x];
    }
    if(x == y) return answer;
    ans = 0;
    if(deep[x] < deep[y]) swap(x, y);
    Poi_A(1, n, 1, tree[y] + 1, tree[x]);
    answer = max(answer, ans);
    return answer;
} 

bool cmp(Node_2 a, Node_2 b){
    return a.dis > b.dis;
}

void debug(){
    for(int i = 1; i <= m; i ++) cout << Ask[i].dis << endl;cout << endl;
    for(int i = 1; i <= m; i ++) cout << Ask[i].Max << endl;
    exit(0);
}

int main()
{
    freopen("gg.in", "r", stdin);
    n = read(); m = read();
    for(int i = 1; i <= n; i ++) head[i] = -1;
    for(int i = 1; i <= n - 1; i ++) {
        int u = read(), v = read(), w = read();
        add(u, v, w); add(v, u, w);
    }
    dfs_find_son(1, 0, 1);
    dfs_to_un(1, 1);
    build_tree(1, n, 1);
    for(int i = 1; i <= m; i ++){
        Ask[i].l = read(); Ask[i].r = read();
        Ask[i].dis = Sec_A_imp(Ask[i].l, Ask[i].r);
    }
    sort(Ask + 1, Ask + m + 1, cmp);
    int B = Ask[1].dis;
    int k = 1;

    while(Ask[k].dis == B){
        Ask[k].Max = Poi_A_imp(Ask[k].l, Ask[k].r);
        k ++;
    }
    //debug();

    for(int i = 1; i <= m; i ++){if(Ask[i].Max) Ask[i].dis -= Ask[i].Max;}
    int answer(0);
    for(int i = 1; i <= m; i ++) answer = max(answer, Ask[i].dis);
    cout << answer;
    return 0;
}
/*
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5
*/

原文地址:https://www.cnblogs.com/shandongs1/p/8157072.html

时间: 2024-10-07 02:33:54

[Luogu] 运输计划--000的相关文章

[luogu]P2680 运输计划

原题链接 :P2680 运输计划 分析 题意很简单,给定一张连通图,n个点,n-1条边,很显然是一棵树. 现在给定m条链\((u,v)\). 现在可以cut掉一条边(边权置为0). 现在求最长链的最小值. 55pts 蒟蒻的我肯定是拿不到满分的..我们直接考虑部分分. 对于m=1的点,我们只需要枚举链上的全部边,然后cut掉最长的那一个就可以了. 裸搜一遍dfs,然后求出链长,最后减去最长边就可以了. 然后是第i条航线链接i和i+1点的点. 很明显整张图形成了一条链. 前缀和维护整张图,这样子可

【NOIP2015提高组】运输计划

https://daniu.luogu.org/problem/show?pid=2680 使完成所有运输计划的时间最短,也就是使时间最长的运输计划耗时最短.最大值最小问题考虑用二分答案,每次check(mid)检查时间最长的运输计划耗时是否小于等于mid,二分出使得check(mid)==true的最小mid值. check函数怎么写是本题的难点.耗时小于mid的运输计划不会影响check的结果.耗时大于mid的运输计划肯定需要改造他们的共同边才有可能使它们耗时都小于mid,而有多条共同边的时

洛谷——P2680 运输计划

https://www.luogu.org/problem/show?pid=2680 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物 流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 t

[noip 2015]运输计划 [LCA][树链剖分]

用了luogu上的题目描述 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物 流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰. 为了鼓励科技创

NOIp2015 运输计划 [LCA] [树上差分] [二分答案]

我太懒了 吃掉了题面 题解 & 吐槽 一道很好的树上差分练习题. 不加fread勉强a过bzoj和luogu的数据,加了fread才能在uoj里卡过去. 可以发现,答案则是运输计划里花费的最大值,最大值最小,便是二分答案的标志. 那么该怎么check呢... 我们得找出所有超过限制的计划,这个过程可以在LCA倍增的过程中预处理出来. 然后再找出一些被这些计划都覆盖的边,找到最大的那条边,如果最大的计划花费减去最大的那条边小于x,那么x就是可行的. 但是该怎么找到那些被计划都覆盖的边呢... 我们

树链剖分-Hello!链剖-[NOIP2015]运输计划-[填坑]

This article is made by Jason-Cow.Welcome to reprint.But please post the writer's address. http://www.cnblogs.com/JasonCow/ [NOIP2015]运输计划    Hello!链剖.你好吗? 题意: 给出一棵n个节点的带权树,m对树上点对 现在允许删除一条边,(权值修改为0) 输出: 最小化的点对间最大距离 1.链剖 2.树上差分 3.二分 链剖我就不多说了,就是两dfs 注意

[NOIP2015] 提高组 洛谷P2680 运输计划

题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物 流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰. 为了鼓励科技创新,L 国国王同意小 P 的

[BZOJ4326][codevs4632][codevs5440][UOJ#150][NOIP2015]运输计划

试题描述 公元 2044 年,人类进入了宇宙纪元. L 国有 n 个星球,还有 n?1 条双向航道,每条航道建立在两个星球之间,这 n?1 条航道连通了 L 国的所有星球. 小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰. 为了鼓励科技创新, L 国国王同意小 P 的物流公司参与

Vijos[1983]NOIP2015Day2T3 运输计划 transport LCA

题目链接Vijos 题目链接UOJ 转载一个大佬的题解: 点击这里->银牌爷题解 主要考察二分查找.树上倍增.贪心."树上前缀和".题目是一颗树,要求将一条边的权值变为0,使得所有运输计划的最大时间最小.直觉告诉我们,这是一个树上倍增的题目,但是它却不像前几年的 Day2T3 开车旅行那样纯倍增,或许更像疫情控制一些,倍增只是辅助算法,还需要配合其他算法.由于要使所有运输计划的最大时间最小,不难想到二分答案的方法.使C(t)表示是否可以改造一条边,使得改造之后所有运输计划中最长的