2024-11-29 16:17阅读: 6评论: 0推荐: 0

AT_abc355_f MST Query 题解

Tag:最小生成树

原题链接

link

题目大意

给你一棵 n 个点的带边权的树,有 q 次询问,每次询问加一条带边权的边,输出当前的最小生成树的边权和。

思路

这道题我们观察题目范围,可知权值的范围很小。所以我们考虑枚举权值,计录这种权值的边对答案的变化 dpi

对于一条边,我们用并查集记录这条边加进去会不会构成环。

  • 如果这条边不会构成环不在最小生成树内,则加入这条边,变化量加上这条边的权值。

  • 如果这条边构成环在最小生成树内,则去掉这条边,变化量减去这条边的权值。

最后对 dp 数组求一遍前缀和就是答案。

代码

#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
int n,q;
int a[500005],b[500005],c[500005],dp[500005];
int num[500005];
int fa[500005];
int find(int x)
{
	if(fa[x]==x) return x;
	fa[x]=find(fa[x]);
	return fa[x];
}
bool merge(int x,int y)
{
	int xx=find(x),yy=find(y);
	if(xx==yy) return false;
	if(num[xx]>num[yy]) swap(xx,yy);
	num[yy]+=num[xx];
	fa[xx]=yy;
	return true;
}
bool fl[500005];
signed main()
{
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=n+q-1;i++)
	{
		cin>>a[i]>>b[i]>>c[i];
	}
	for(int i=1;i<=10;i++)
	{
		for(int j=1;j<=200005;j++) 
		{
			fa[j]=j;
			num[j]=1;
		}
		for(int j=1;j<=n+q-1;j++)
		{
			if(c[j]<=i)
			{
				if(merge(a[j],b[j]))
				{
					if(!fl[j])
					{
						fl[j]=1;
						dp[j]+=i;
					}
				}
				else
				{
					if(fl[j])
					{
						dp[j]-=i;
						fl[j]=0;
					}
				}
			}
		}
	}
	for(int i=1;i<=n+q-1;i++) dp[i]+=dp[i-1];
	for(int i=n;i<=n+q-1;i++)
	{
		cout<<dp[i]<<"\n";
	}
	return 0;
}



本文作者:yaaaaaan

本文链接:https://www.cnblogs.com/yaaaaaan/p/18577000

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   yaaaaaan  阅读(6)  评论(0编辑  收藏  举报
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
🔑
点击右上角即可分享
微信分享提示