• P2680 运输计划


    P2680 运输计划

    给定一棵树和若干条确定的路径, 你可以将树上一条边权值变为 (0) , 求变完以后给定路径的最长长度最短


    调试日志:
    1. 树剖部分, 对应到点的编号用了对应到线段树上的编号
    2. 二分写反了(虽然很快就看到了)
    简记: 对应到树上不管什么都用 (pos) , 对应到点上不管什么都用 (ori)


    Solution

    艹, 考前心态炸裂, 连续状态四天奇差, 颓了个星期天, 回来打代码风生水起 (1A) 就是nima的爽
    哦还有祝贺 (IG)


    最大值最小, 联想到二分答案
    二分一个 (k)
    具体的, 我们检查给定路径中长度大于 (k), 若是这些路径有 (s)
    现在这 (s) 条路径都不符合条件, 所以要是要将一条边的边权置为 (0), 必须是这 (s) 条边的交边
    不然就没法将这 (s) 条边总值一起变小了嘛
    所以检查思路为: 对于一个 (k), 找出大于 (k) 的边的交边, 若最大边 - 交边 (leq k) , 则 (k) 合法

    获取给定路径用树剖加线段树
    注意边权下放处理
    检查时因为是多次修改单次查询, 可以用树剖留下来的连续区间 + 差分 实现
    差分数组前缀和即为下放到ori[i]点的路径的覆盖数

    Code

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    #define REP(i, x, y) for(int i = (x);i <= (y);i++)
    using namespace std;
    int RD(){
        int out = 0,flag = 1;char c = getchar();
        while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
        while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
        return flag * out;
        }
    const int maxn = 600019,INF = 1e9 + 19;
    int head[maxn],nume = 1;
    struct Node{
        int v,dis,nxt;
        }E[maxn << 3];
    void add(int u,int v,int dis){
        E[++nume].nxt = head[u];
        E[nume].v = v;
        E[nume].dis = dis;
        head[u] = nume;
        }
    int num, nr;
    int dep[maxn], fa[maxn], size[maxn], val[maxn], top[maxn], wson[maxn];
    int tot, pos[maxn], ori[maxn];
    void dfs1(int u, int F){
    	size[u] = 1;
    	for(int i = head[u];i;i = E[i].nxt){
    		int v = E[i].v;
    		if(v == F)continue;
    		dep[v] = dep[u] + 1;
    		fa[v] = u;
    		val[v] = E[i].dis;//边权下放
    		dfs1(v, u);
    		size[u] += size[v];
    		if(size[v] > size[wson[u]])wson[u] = v;
    		}
    	}
    void dfs2(int u, int TP){
    	pos[u] = ++tot;
    	ori[tot] = u;
    	top[u] = TP;
    	if(!wson[u])return ;
    	dfs2(wson[u], TP);
    	for(int i = head[u];i;i = E[i].nxt){
    		int v = E[i].v;
    		if(v == fa[u] || v == wson[u])continue;
    		dfs2(v, v);
    		}
    	}
    #define lid (id << 1)
    #define rid (id << 1) | 1
    struct seg_tree{
    	int l, r;
    	int sum;
    	}tree[maxn << 2];
    void pushup(int id){tree[id].sum = tree[lid].sum + tree[rid].sum;}
    void build(int id, int l, int r){
    	tree[id].l =l, tree[id].r = r;
    	if(l == r){
    		tree[id].sum = val[ori[l]];
    		return ;
    		}
    	int mid = (l + r) >> 1;
    	build(lid, l, mid), build(rid, mid + 1, r);
    	pushup(id);
    	}
    int query(int id, int l, int r){
    	if(tree[id].l == l && tree[id].r == r)return tree[id].sum;
    	int mid = (tree[id].l + tree[id].r) >> 1;
    	if(mid < l)return query(rid, l, r);
    	else if(mid >= r)return query(lid, l, r);
    	return query(lid, l, mid) + query(rid, mid + 1, r);
    	}
    int Q_sum(int x, int y){
    	int ret = 0;
    	while(top[x] != top[y]){
    		if(dep[top[x]] < dep[top[y]])swap(x, y);
    		ret += query(1, pos[top[x]], pos[x]);
    		x = fa[top[x]];
    		}
    	if(x == y)return ret;
    	if(dep[x] > dep[y])swap(x, y);
    	ret += query(1, pos[x] + 1, pos[y]);
    	return ret;
    	}
    struct Edge{
    	int u, v, dis;
    	}I[maxn];
    int D[maxn], maxx;//差分数组
    void uprange(int x, int y){
    	while(top[x] != top[y]){
    		if(dep[top[x]] < dep[top[y]])swap(x, y);
    		D[pos[top[x]]] += 1;
    		D[pos[x] + 1] -= 1;
    		x = fa[top[x]];
    		}
    	if(x == y)return ;
    	if(dep[x] > dep[y])swap(x, y);
    	D[pos[x] + 1] += 1;
    	D[pos[y] + 1] -= 1;
    	}
    bool check(int k){
    	int n = 0;
    	memset(D, 0, sizeof(D));
    	while(I[n + 1].dis > k){
    		n++;
    		uprange(I[n].u, I[n].v);
    		}
    	int sum = 0;
    	REP(i, 1, num){
    		sum += D[i];//差分数组前缀和即为下放到ori[i]点的路径的覆盖数
    		if(sum == n && maxx - val[ori[i]] <= k)return 1;
    		}
    	return 0;
    	}
    int search(int l, int r){
    	int ans = -1;
    	while(l <= r){
    		int mid = (l + r) >> 1;
    		if(check(mid))ans = mid, r = mid - 1;
    		else l = mid + 1;
    		}
    	return ans;
    	}
    bool cmp(Edge a, Edge b){return a.dis > b.dis;}
    int main(){
    	num = RD(), nr = RD();
    	REP(i, 1, num - 1){
    		int u = RD(), v = RD(), dis = RD();
    		add(u, v, dis), add(v, u, dis);
    		}
    	dep[1] = 1;
    	dfs1(1, -1);
    	dfs2(1, 1);
    	build(1, 1, num);
    	REP(i, 1, nr){
    		I[i].u = RD(), I[i].v = RD(), I[i].dis = Q_sum(I[i].u, I[i].v);
    		}
    	sort(I + 1, I + 1 + nr, cmp);
    	maxx = I[1].dis;//头头
    	printf("%d
    ", search(0, maxx));
    	return 0;
    	}
    

    CG

  • 相关阅读:
    centos崩溃后如何修复
    乘法是啥
    接上篇—用spring注入DBbean,并使用maven管理
    技术产生价值
    技术?
    世界的本质是啥呢
    java-web 登陆功能
    对java的理解
    数学的历史
    使用jmeter测试接口
  • 原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9867384.html
Copyright © 2020-2023  润新知