首页 > 其他分享 >[ARC098F] Donation(找性质+点 Kruskal 重构树)

[ARC098F] Donation(找性质+点 Kruskal 重构树)

时间:2022-11-04 20:46:15浏览次数:107  
标签:le Point int Kruskal Donation num Maxn ARC098F dp

[ARC098F] Donation

给出一个 \(N\) 个点 \(M\) 条边的无向连通图,每个点的标号为 \(1\) 到 \(n\), 且有两个权值 \(A_i,B_i\)。第 \(i\) 条边连接了点 \(u_i\) 和 \(v_i\)。

最开始时你拥有一定数量的钱,并且可以选择这张图上的任意一个点作为起始点,之后你从这个点开始沿着给定的边遍历这张图。每当你到达一个点 \(v\) 时,你必须拥有至少 \(A_v\) 元。而当你到达了这个点后,你可以选择向它捐献 \(B_v\) 元(当然也可以选择不捐献),当然,你需要保证在每次捐献之后自己剩余的钱\(\geq 0\)。

你需要对所有的 \(n\) 个点都捐献一次,求你一开始至少需要携带多少钱。

\(1\le N\le 10^5,N-1\le M\le 10^5,1\le A_i,B_i\le 10^9,1\le u_i<v_i\le N\),保证题目给出的图联通。

考虑倒着来,那么每个点的限制变成了需要至少 \(C_i=\max\{A_i-B_i,0\}\) 钱才能到达,到了之后能够得到 \(B_i\) 块钱。

\(\bigstar\texttt{Hint}\):后面没有想到的主要原因是,没发现如果到达了一个 \(C_i\) 的点,则所有 \(\le C_i\) 的点都可以随意经过。

这样想到用 \(\text{Kruskal}\) 重构树建立连通性,越往上 \(C\) 越大,在树上判断。

答案一定是由一个节点向根节点方向走,到达 \(u\) 后将 \(u\) 的所有子树遍历后在向上走。

设 \(dp_{i}\) 表示从 \(i\) 子树中一点走到 \(i\) 所需要的最小初始钱数,\(S_i\) 表示 \(i\) 为根的子树的 \(B\) 之和,则根据儿子们如下转移:

  • 如果自己是叶子节点,则 \(dp_{i}=C_i\)。
  • 如果不是,则 \(dp_{i}=\min_v\{\max(dp_i,C_i-S_v)\}\)。
#define Maxn 100005
int n,m,tot,rt;
int bel[Maxn],A[Maxn],B[Maxn],C[Maxn],fa[Maxn];
int hea[Maxn],nex[Maxn<<1],ver[Maxn<<1];
ll dp[Maxn],sum[Maxn];
vector<int> g[Maxn];
struct Point
{
	int num,a,b,c;
	Point(int _num=0,int _a=0,int _b=0,int _c=0):num(_num),a(_a),b(_b),c(_c){}
	bool friend operator < (Point x,Point y)
		{ return (x.c!=y.c)?x.c<y.c:x.num<y.num; }
}d[Maxn];
int Find(int x){ return (fa[x]==x)?x:(fa[x]=Find(fa[x])); }
inline void add(int x,int y){ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot; }
void dfs(int x)
{
	if(!g[x].size()) { sum[x]=B[x],dp[x]=C[x]; return; }
	sum[x]=B[x],dp[x]=infll;
	for(int v:g[x])
		dfs(v),sum[x]+=sum[v],
		dp[x]=min(dp[x],max(dp[v],C[x]-sum[v]));
}
int main()
{
	n=rd(),m=rd();
	for(int i=1;i<=n;i++)
		A[i]=rd(),B[i]=rd(),C[i]=max(A[i]-B[i],0),
		d[i]=Point(i,A[i],B[i],C[i]),fa[i]=i;
	for(int i=1,x,y;i<=m;i++) x=rd(),y=rd(),add(x,y),add(y,x);
	sort(d+1,d+n+1);
	for(int p=1,x,y;p<=n;p++)
	{
		x=d[p].num;
		for(int i=hea[x];i;i=nex[i])
		{
			y=Find(ver[i]);
			if(x!=y && (C[y]<C[x] || (C[y]==C[x] && y<x)))
				g[x].pb(y),fa[y]=x;
		}
	}
	rt=d[n].num,dfs(rt);
	printf("%lld\n",dp[rt]+sum[rt]);
	return 0;
}

标签:le,Point,int,Kruskal,Donation,num,Maxn,ARC098F,dp
From: https://www.cnblogs.com/EricQian/p/16859050.html

相关文章

  • 福建WC2014 路径权值(Kruskal重构树 + 树状数组)
    题目描述:给定一个带权树,树上任意两点间的路径权值\(d\left(x,y\right)\)定义为\(x,y\)这两个点之间路径上的最小值,树上任意一点x的权值定义为这个点到树上其他所有点......
  • BZOJ P3732 Network(Kruskal重构树)
    Network题目描述:给\(N\)个点的无向图\(\left(1\leqN\leq15000\right)\),记为:\(1\dotsN\)图中有\(M\)条边\(\left(1\leqM\leq30000\right)\),第\(i\)......
  • 【XSY3810】公路建设(线段树,kruskal)
    题面题解一开始竟然没反应过来是最小生成树。考虑用线段树直接维护每个区间的答案。注意到一个区间最多只有\(n-1\)条树边有用,所以线段树每个节点用一个vector按权......
  • MST算法-堆优化Prim与并查集优化Kruskal
    生成树首先,生成树是相对于连通图来说的。它是一个连通图的子图,而且没有环,也可以看成是一棵树。对于生成树,其所有结点的根节点都是同一个。生成树有以下两个性质:生成树......
  • 859 Kruskal求最小生成树
    把所有边按照权重值从小到大排序,然后一一收入集合,利用联通集判断一条边的两个点是否在一个联通集中如果在就不收录这条边#include<bits/stdc++.h>usingnamespacestd;......
  • Kruskal 重构树 学习笔记
    参考资料:Kruskal重构树学习笔记-ImmortalWatcher定义顾名思义,Kruskal重构树就是用Kruskal生成树算法在无向图上得到的树所构建出的新树。Kruskal重构树具有良好......
  • HDU 1863 畅通工程(Kruskal)
    畅通工程TimeLimit:1000/1000MS(Java/Others)    MemoryLimit:32768/32768K(Java/Others)TotalSubmission(s):23823    AcceptedSubmission(s):10381Pr......
  • Kruskal重构树 学习笔记
    我们回顾一下最小与最大生成树的性质:对于一张图的最小生成树,原图中任意两个节点中任意一条路径的边权最大值的最小值为生成树中节点路径间边权的最大值。最大生成树则相反......
  • ABC-270 F - Transportation(kruskal)
    ABC-270F-Transportation(kruskal)考虑等价转换,建立两个虚点(分别表示airport和harbor的中转站)。这样就可以把点统一为边权问题。对于操作1和操作2,就是等价于向虚点连边......
  • kruskal重构树and笛卡尔树
    为什么放在了一起因为我个人旗帜鲜明的认为这是一个东西前者可以旗帜鲜明的解决图上(包括树上)限定最值的联通问题建立方式:跑kruskal时每连接两个点时建立一个新点把原......