题目大意:
给定一个图,每次修改一条边的边权,每次求最小生成树。
思路:
神仙题目。
虽然代码的形式就是分治,但是和普通的分治还是有区别的。
考虑kruskal是怎么求最小生成树的,即暴力排序之后利用并查集维护连通信息。
所以这里的最裸的暴力就是每一次修改一条边的边权之后重新排序,再重新用并查集维护信息。
这题的一切都是从上面的这个想法出发的,所以现在考虑如何优化上述算法。
不难发现对于一段连续的修改,其中有大部分的信息是不变的,同时,不变的信息也随着修改的区间的长度的增长而减少。
对于一段[l,r]的修改操作,假设原来的图的规模为n,如果我们能够每次把最小生成树的公共信息提取出来,即哪些边一定会再最小生成树上面,哪些边一定不会在最小生成树上面,这样以后图的规模会缩小到(r-l+1)级别,如果我们要处理这一区间内的询问,只需要对这个(r-l+1)规模的图进行操作。
于是一个利用重复的信息来减少复杂度的算法就可以得到了,即不断地将询问分成若干部分,假设分成了d部分,那么会将图分成了q/d个不同时间段图,每一个图的规模大小为n/d,同时得到了q/d个子问题。
现在考虑如何求解一定在MST上的边,即将所有的不确定边设为-inf之后仍在MST上的边。
至于一定不在MST上的边,即将所有的不确定边设为inf后仍不在MST上的边。
所以缩减图的规模其实十分耗时,所以这里把d设为了2。(其实这里我也不知道为什么,并不知道怎么分析时间复杂度)。
#include<bits/stdc++.h>
#define REP(i,a,b) for(register int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(register int i=a;i##_end_=b;i>=i##_end_;--i)
#define pii pair<int,ll>
#define fi first
#define se second
#define mk make_pair
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj2001.in","r",stdin);
freopen("bzoj2001.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='0')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=2e4+10;
const int maxm=5e4+10;
int n,m,q;
pii qu[maxm];
int tot[22],bel[22][maxn],s[22][maxm],t[maxm];
bool in[maxm];
int fa[maxn];
ll ans[maxm];
struct node{
int u,v,ty;
ll w;
}e[maxm];
bool cmp(int x,int y){
if(e[x].ty!=e[y].ty)return e[x].ty<e[y].ty;
return e[x].w<e[y].w;
}
int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}
ll kruskal(int siz,int dep){
ll ret=0;
sort(t+1,t+siz+1,cmp);
REP(i,1,siz){
int u=e[t[i]].u,v=e[t[i]].v;
u=bel[dep][u]; v=bel[dep][v];
fa[u]=u; fa[v]=v; in[t[i]]=0;
}
REP(i,1,siz){
int u=e[t[i]].u,v=e[t[i]].v;
u=bel[dep][u]; v=bel[dep][v];
if(find(u)==find(v))continue;
fa[find(u)]=find(v);
ret+=e[t[i]].w;
in[t[i]]=1;
}
return ret;
}
ll merge(int l,int r,int dep){
ll ret=0;
REP(i,1,tot[dep])t[i]=s[dep][i];
REP(i,l,r)e[qu[i].fi].ty=-1;
kruskal(tot[dep],dep-1);
REP(i,1,tot[dep]){
int u=bel[dep-1][e[t[i]].u],v=bel[dep-1][e[t[i]].v];
fa[u]=u; fa[v]=v;
}
REP(i,1,tot[dep]){
if(!in[t[i]] || e[t[i]].ty==-1)continue;
int u=bel[dep-1][e[t[i]].u],v=bel[dep-1][e[t[i]].v];
ret+=e[t[i]].w;
fa[find(u)]=find(v);
}
REP(i,1,tot[dep]){
int u=e[t[i]].u,v=e[t[i]].v;
int uu=bel[dep-1][u],vv=bel[dep-1][v];
bel[dep][u]=find(uu); bel[dep][v]=find(vv);
}
int sz=0;
REP(i,1,tot[dep]){
if(in[t[i]] && e[t[i]].ty!=-1)continue;
s[dep][++sz]=t[i];
}
tot[dep]=sz;
REP(i,l,r)e[qu[i].fi].ty=0;
return ret;
}
void erase(int l,int r,int dep){
REP(i,1,tot[dep])t[i]=s[dep][i];
REP(i,l,r)e[qu[i].fi].ty=1;
kruskal(tot[dep],dep-1);
int sz=0;
REP(i,1,tot[dep]){
if(!in[t[i]] && e[t[i]].ty!=1)continue;
s[dep][++sz]=t[i];
}
tot[dep]=sz;
REP(i,l,r)e[qu[i].fi].ty=0;
}
void divide(int l,int r,int dep,ll sum){
tot[dep]=tot[dep-1];
REP(i,1,tot[dep]){
s[dep][i]=s[dep-1][i];
int u=e[s[dep][i]].u,v=e[s[dep][i]].v;
bel[dep][u]=bel[dep-1][u];
bel[dep][v]=bel[dep-1][v];
}
ll val=merge(l,r,dep);
erase(l,r,dep);
if(l==r){
e[qu[l].fi].w=qu[l].se;
REP(i,1,tot[dep])t[i]=s[dep][i];
ans[l]=kruskal(tot[dep],dep)+sum+val;
return;
}
int mid=(l+r)>>1;
divide(l,mid,dep+1,sum+val);
divide(mid+1,r,dep+1,sum+val);
}
void init(){
read(n); read(m); read(q);
REP(i,1,m)read(e[i].u),read(e[i].v),read(e[i].w);
REP(i,1,q)read(qu[i].fi),read(qu[i].se);
tot[0]=m;
REP(i,1,n)bel[0][i]=i;
REP(i,1,m)s[0][i]=i;
}
int main(){
File();
init();
divide(1,q,1,0);
REP(i,1,q)printf("%lld\n",ans[i]);
//cerr<<(double)clock()/CLOCKS_PER_SEC<<endl;
return 0;
}