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;
}