原題連結
link
題目大意
給你一棵 \(n\) 個點的帶邊權的樹,有 \(q\) 次詢問,每次詢問加一條帶邊權的邊,輸出當前的最小生成樹的邊權和。
思路
這道題我們觀察題目範圍,可知權值的範圍很小。所以我們考慮列舉權值,計錄這種權值的邊對答案的變化 \(dp_i\)。
對於一條邊,我們用並查集記錄這條邊加進去會不會構成環。
-
如果這條邊不會構成環且不在最小生成樹內,則加入這條邊,變化量加上這條邊的權值。
-
如果這條邊構成環且在最小生成樹內,則去掉這條邊,變化量減去這條邊的權值。
最後對 \(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;
}