题目描述
Rinne 最近了解了如何快速维护可支持插入边删除边的图,并且高效的回答一下奇妙的询问。
她现在拿到了一个 n 个节点 m 条边的无向连通图,每条边有一个边权 w
现在她想玩一个游戏:选取一个 “重要点” S,然后选择性删除一些边,使得原图中所有除 S 之外度为 1 的点都不能到达 S。
定义删除一条边的代价为这条边的边权,现在 Rinne 想知道完成这个游戏的最小的代价,这样她就能轻松到达 rk1 了!作为回报,她会让你的排名上升一定的数量。
题解
m=n-1并且图联通,那么这个图就是一棵树,所以本题可转换为,以S为根的树,求所有叶子节点与S不连通的最小代价。
那么我们可以将每条边的边权转换为点权(架空根节点S,所有边权指向深度较大的那个点),然后代价就变成每颗子树的代价和,或者是当前节点的权值,两者取最小的就可以了!像树形dp那样~
代码
#include<bits/stdc++.h> #define ls rt<<1 #define rs rt<<1|1 #define pb push_back #define fi first #define se second #define ios ios::sync_with_stdio(0); using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef vector<int> VI; const int maxn = 2e6 + 6; const LL inf = 0x3f3f3f3f3f3f3f3f; //const int mod = 998244353; //LL qp(LL x,LL y){LL ans=1;x%=mod;while(y){if(y&1) ans=ans*x%mod;x=x*x%mod;y>>=1;}return ans;} //head struct Edge{ int to,w,next; }edge[maxn]; int tot,head[maxn]; void add(LL u,LL v,LL w){ edge[++tot]={v,w,head[u]}; head[u]=tot; } LL n,m,s,a[maxn],dp[maxn]; VI G[maxn]; void dfs(int u,int fa,LL w){ dp[u]=w; LL mi=0; for(int i=head[u];i;i=edge[i].next){ LL v=edge[i].to,w=edge[i].w; if(v==fa) continue; dfs(v,u,w); mi+=dp[v]; } if(mi) dp[u]=min(dp[u],mi); } int main(){ scanf("%lld%lld%lld",&n,&m,&s); for(LL i=1,u,v,w;i<n;i++){ scanf("%lld%lld%lld",&u,&v,&w); add(u,v,w);add(v,u,w); } dfs(s,-1,inf); printf("%lld\n",dp[s]); }