很妙的題目。
首先可以將詢問給離線下來,將一條邊的邊權更改變為在 \([l,r]\) 的時間段中的權值為 \(w\),所以考慮線段樹分治。
然後我們發現要求的就是每個時間的最小生成樹,那我們如何最佳化複雜度呢?
我們有一個容易想到的方法:
-
對於時間段 \([l,r]\),設這段時間中未被更改的邊集為 \(E\),那麼若只對 \(E\) 中的邊做最小生成樹(kruskal),如果邊 \((u_i,v_i,w_i)\) 未被加入到樹邊當中,那麼這條邊一定不會被選擇;
-
如果讓其他不在 \(E\) 中的邊(也就是被改動過的邊)先進行 kruskal,然後再對 \(E\) 中的邊嘗試加入樹邊,那如果邊 \((u_i,v_i,w_i)\) 被加入到最小生成樹中,就說明這條邊必然會被加入。然後把 \(E\) 中不屬於這兩種的邊繼續往下遞迴。
貌似這個做法很玄乎,但經過證明,我們發現這樣做是對的,以下為證明:
操作二使得連通塊個數不會超過 \(r-l\),而操作一則是刪掉了沒必要的邊使邊數連通塊數與連通塊數同級,故複雜度正確。
考慮使用線段樹和可撤銷並查集維護即可。
點選檢視程式碼
#include<bits/stdc++.h>
#define fir first
#define sec second
#define int long long
#define lowbit(x) x&(-x)
#define mkp(a,b) make_pair(a,b)
using namespace std;
typedef pair<int,int> pir;
inline int read(){
int x=0,f=1; char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1; c=getchar();}
while(isdigit(c)){x=x*10+(c^48); c=getchar();}
return x*f;
}
const int inf=1e18,N=5e4+5;
int n,m,q;
struct dsu{int a,b,val;}st[N];
int fa[N],siz[N],top,res;
inline int getfa(int x){
if(fa[x]!=x) return getfa(fa[x]);
return fa[x];
}
inline bool merge(int u,int v,int w){
int a=getfa(u),b=getfa(v);
if(a==b) return 0;
if(siz[a]<siz[b]) swap(a,b);
siz[a]+=siz[b],fa[b]=a,res+=w;
st[++top]=dsu{a,b,w};
return 1;
}
inline void del(){
auto [a,b,val]=st[top];
siz[a]-=siz[b],fa[b]=b,res-=val;
top--;
}
struct edge{int u,v,w;}s[N];
inline bool cmp(edge a,edge b){return a.w<b.w;}
struct tree{
vector<edge> e[N<<2];
inline void add(int l,int r,int p,int ll,int rr,edge k){
if(ll>rr) return ;
if(ll<=l&&r<=rr){
e[p].push_back(k);
return ;
}
int mid=(l+r)>>1;
if(ll<=mid) add(l,mid,p<<1,ll,rr,k);
if(rr>mid) add(mid+1,r,p<<1|1,ll,rr,k);
}
inline void solve(int l,int r,int p){
int T=top;
sort(e[p].begin(),e[p].end(),cmp);
if(l==r){
for(auto [u,v,w]:e[p]) merge(u,v,w);
cout<<res<<'\n';
while(top>T) del();
assert(top==T);
return ;
}
int sz=e[p].size();
vector<int> vv;
vv.resize(sz);
for(int i=0;i<sz;i++) vv[i]=0;
for(int i=0;i<sz;i++){
auto [u,v,w]=e[p][i];
if(!merge(u,v,0)) vv[i]=1;
}
while(top>T) del();
for(int i=l;i<=r;i++){
auto [u,v,w]=s[i];
merge(u,v,0);
}
for(int i=0;i<sz;i++){
if(vv[i]) continue;
auto [u,v,w]=e[p][i];
if(merge(u,v,0)) vv[i]=2;
}
while(top>T) del();
for(int i=0;i<sz;i++){
auto [u,v,w]=e[p][i];
if(vv[i]==2) merge(u,v,w);
if(vv[i]==0){
e[p<<1].push_back(e[p][i]);
e[p<<1|1].push_back(e[p][i]);
}
}
int mid=(l+r)>>1;
solve(l,mid,p<<1);
solve(mid+1,r,p<<1|1);
while(top>T) del();
}
}Tr;
int lst[N];
int u[N],v[N],w[N];
signed main(){
n=read(),m=read(),q=read();
for(int i=1;i<=m;i++) u[i]=read(),v[i]=read(),w[i]=read(),lst[i]=1;
for(int i=1;i<=q;i++){
int id=read(),x=read();
if(i>1) Tr.add(1,q,1,lst[id],i-1,edge{u[id],v[id],w[id]});
lst[id]=i,w[id]=x;
s[i]=edge{u[id],v[id],w[id]};
}
for(int i=1;i<=m;i++) Tr.add(1,q,1,lst[i],q,edge{u[i],v[i],w[i]});
for(int i=1;i<=n;i++) siz[i]=1,fa[i]=i;
Tr.solve(1,q,1);
}