1 条题解
-
0
自动搬运
来自洛谷,原作者为

sprads
meaningless搬运于
2025-08-24 22:09:20,当前版本为作者最后更新于2022-03-24 19:17:43,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这是一篇点分治 + 双指针的题解。
计算路径
设点分治中,当前找到的根为 , 表示从 到 的点权和, 则表示从 到 的边权和。
分两种情况考虑合法性:
-
从 走到 :显然需要对于任意在 到 路径上的节点 ,满足 。稍加变换得:。此时只需要维护、记录最大的 即可判断从 到 的合法性。
-
从 走到 :这种情况,路径的起点不一定是 ,可以从其他子树中的一个节点 。设 后还剩下的油量为 (直接从 出发那么 为 ),则 路径上任意一点 ,需要满足 。依旧是稍加变换:,维护、记录路径上最小的 即可。
统计答案
遍历所有子树,把所有路径都记录下来。
从 到 记录 ,表示到 后的剩余油量。
从 到 记录 的最小值。
分别将两种情况按记录的权值排序,用 匹配 (反过来也可以)。
此时对于所有 的 都可以,双指针扫描统计即可。
注意要将 和 在同一子树内的情况提前减去。
代码
#include<bits/stdc++.h> #define ll long long using namespace std; const int N = 100005; int rd(){ int x = 0;char ch = getchar(); while(ch < '0' || ch > '9')ch = getchar(); while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x; } ll s1[N],s2[N],q1[N],q2[N],w[N],d[N],ans; int tot,head[N],ver[2*N],edge[2*N],Next[2*N]; int n,a[N],rt,sum,s[N],Mas[N],vis[N],t1,t2,h1,h2; void add(int x,int y,int z){ tot++; ver[tot] = y; edge[tot] = z; Next[tot] = head[x]; head[x] = tot; } void Grt(int fa,int x){ s[x] = 1,Mas[x] = 0; for(int i = head[x];i;i = Next[i]){ int y = ver[i]; if(y == fa || vis[y])continue; Grt(x,y); s[x] += s[y]; Mas[x] = max(Mas[x],s[y]); } Mas[x] = max(Mas[x],sum - s[x]); if(!rt || Mas[x] < Mas[rt])rt = x; } void dfs(int fa,int x,ll Max,ll Min){//Max 最大 w[j] - d[j];Min 最小 w[fa[j]]-d[j] if(w[x] - d[x] >= Max)//合法才记录 s1[++t1] = w[x] - d[x];//i到rt s2[++t2] = Min;//rt到i,目前不能确定是否合法,都记录 for(int i = head[x];i;i = Next[i]){ int y = ver[i],z = edge[i]; if(y == fa || vis[y])continue; d[y] = d[x] + z,w[y] = w[x] + a[y]; dfs(x,y,max(Max,w[x] - d[x]),min(Min,w[x] - d[y]));//注意更新 Max 和 Min } } void calc(int x){ int L; h1 = 0,h2 = 0; d[x] = 0,w[x] = a[x]; for(int i = head[x];i;i = Next[i]){ int y = ver[i],z = edge[i]; if(vis[y])continue; t1 = 0,t2 = 0; d[y] = z,w[y] = w[x] + a[y];//初始化d[y]和w[y] dfs(x,y,w[x] - d[x],w[x] - d[y]); sort(s1 + 1,s1 + 1 + t1);//s1、s2记录当前子树内的路径 sort(s2 + 1,s2 + 1 + t2); L = 1; for(int i = t2;i >= 1;i--){//注意倒序枚举 while(L <= t1 && s1[L] + s2[i] - a[x] < 0)L++; ans -= t1 - L + 1;//减去同一子树内的答案 } for(int i = 1;i <= t1;i++) q1[++h1] = s1[i]; for(int i = 1;i <= t2;i++) q2[++h2] = s2[i];//q1和q2记录所有路径 } sort(q1 + 1,q1 + 1 + h1); sort(q2 + 1,q2 + 1 + h2); L = 1; for(int i = h2;i >= 1;i--){ if(q2[i] >= 0)//rt到i直接从rt出发 ans++; while(L <= h1 && q1[L] + q2[i] - a[x] < 0)L++;//i到rt,rt到i都加了a[x],减掉一次 ans += h1 - L + 1; } ans += h1;//i出发到rt结束的情况 } void solve(int x){ vis[x] = 1,calc(x); for(int i = head[x];i;i = Next[i]){ int y = ver[i],z = edge[i]; if(vis[y])continue; rt = 0,sum = s[y]; Grt(x,y),solve(rt); } } int main(){ n = rd(); for(int i = 1;i <= n;i++) a[i] = rd(); for(int i = 1;i < n;i++){ int x = rd(),y = rd(),z = rd(); add(x,y,z); add(y,x,z); } rt = 0,sum = n; Grt(0,1),solve(rt); printf("%lld\n",ans); return 0; } -
- 1
信息
- ID
- 4295
- 时间
- 1000ms
- 内存
- 250MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者