luogu P2680 運輸計劃
阿新 • • 發佈:2020-10-04
傳送
這道題有多種做法,我的做法是二分+樹上差分,複雜度比較優秀。
二分容易想到,因為如果在當前時間內能完成,那麼大於他的時間內一定可以完成,所以答案符合單調性。
二分後,我們統計當前大於二分值\(mid\)的路徑,假設有\(k\)條,那麼我們需要找到這\(k\)條路徑的一條公共邊,使刪去這條邊後的最長路徑的長度小於\(mid\)。如果存在這一種方案,那麼返回true,繼續向左二分;否則向右二分。
每一條路徑的長度可以用lca來預處理;找到\(k\)條路徑的公共邊可以用樹上差分實現(不用樹剖)。這樣這題就解決了。
我想他之所以是紫題的原因在於樹剖的實現較為複雜吧。
#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<cstring> #include<cstdlib> #include<cctype> #include<vector> #include<queue> #include<assert.h> #include<ctime> using namespace std; #define enter puts("") #define space putchar(' ') #define Mem(a, x) memset(a, x, sizeof(a)) #define In inline #define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt) typedef long long ll; typedef double db; const int INF = 0x3f3f3f3f; const db eps = 1e-8; const int maxn = 3e5 + 5; const int N = 19; In ll read() { ll ans = 0; char ch = getchar(), las = ' '; while(!isdigit(ch)) las = ch, ch = getchar(); while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); if(las == '-') ans = -ans; return ans; } In void write(ll x) { if(x < 0) x = -x, putchar('-'); if(x >= 10) write(x / 10); putchar(x % 10 + '0'); } In void MYFILE() { #ifndef mrclr freopen(".in", "r", stdin); freopen(".out", "w", stdout); #endif } int n, m; struct Edge { int nxt, to, w; }e[maxn << 1]; int head[maxn], ecnt = -1; In void addEdge(int x, int y, int w) { e[++ecnt] = (Edge){head[x], y, w}; head[x] = ecnt; } int dep[maxn], sum[maxn], w[maxn], fa[N + 2][maxn]; In void dfs(int now, int _f) { for(int i = 1; (1 << i) <= dep[now]; ++i) fa[i][now] = fa[i - 1][fa[i - 1][now]]; forE(i, now, v) { if(v == _f) continue; sum[v] = sum[now] + (w[v] = e[i].w); dep[v] = dep[now] + 1, fa[0][v] = now; dfs(v, now); } } In int lca(int x, int y) { if(dep[x] < dep[y]) swap(x, y); for(int i = N; i >= 0; --i) if(dep[fa[i][x]] >= dep[y]) x = fa[i][x]; if(x == y) return x; for(int i = N; i >= 0; --i) if(fa[i][x] ^ fa[i][y]) x = fa[i][x], y = fa[i][y]; return fa[0][x]; } struct Node { int x, y, z, len; }t[maxn]; int dif[maxn], Max = 0; In void dfs2(int now, int _f) { forE(i, now, v) if(v ^ _f) dfs2(v, now), dif[now] += dif[v]; } In bool judge(int x) { // printf("---%d---\n", x); fill(dif + 1, dif + n + 1, 0); int cnt = 0; for(int i = 1; i <= m; ++i) if(t[i].len > x) { ++cnt; dif[t[i].x]++, dif[t[i].y]++, dif[t[i].z] -= 2; } dfs2(1, 0); for(int i = 1; i <= n; ++i) if(dif[i] >= cnt && Max - w[i] <= x) return 1; return 0; } int main() { // MYFILE(); Mem(head, -1), ecnt = -1; n = read(), m = read(); for(int i = 1; i < n; ++i) { int x = read(), y = read(), w = read(); addEdge(x, y, w), addEdge(y, x, w); } dep[1] = 1, dfs(1, 0); for(int i = 1; i <= m; ++i) { int x = read(), y = read(); int z = lca(x, y); t[i] = (Node){x, y, z, sum[x] + sum[y] - (sum[z] << 1)}; Max = max(Max, t[i].len); } int L = 0, R = Max; while(L < R) { int mid = (L + R) >> 1; if(judge(mid)) R = mid; else L = mid + 1; } write(L), enter; return 0; }