传送门:bzoj2001
题解
此题LCT动态维护MST貌似很可做(然而本蒟蒻不会)
这里分享看到的一种很妙的分治做法。
我们简化一下问题:
每次修改
权值,若能使MST总边权变小,则满足:
原MST上
路径上最大值大于
权值
(仔细想想,感觉非常有道理:
若大于等于
路径上,即使可能大于MST中最小边权,但对于更新总边权不会有任何贡献,
因为
只能替换
路径上的边。
)
若使MST总边权变大,这个不好操作,所以我们就预处理后倒着做(离线操作)。
我们cdq的时候,要不断缩小当前需要操作MST的边集,可以每次分治调用一下两个操作。
若当前范围为 ~ 。
1: :
把修改L-R所影响到的所有边的边权设为INF,然后跑MST,则此时都选不上的不在修改边集里的边直接舍去,因为不管怎样这些边都不会被选中,假设现在还没有没舍去的边有cnt条,操作的时候减一减就好了。2: :
把修改L-R所影响到的所有边的边权设为-INF,然后跑MST,则此时都选得上的不在修改边集里的边直接选中,因为不管怎样这些边都会被选中,在下一层分治操作中就不用管这些边了,我们假设所有锁定边为must,往下传就好了。
:
Solve(l,r,G);G当前选中的边的集合(上层分治Reduction舍去了一些边 Contraction 选定了一些边,所以不为全集)
Reduction(G);
Contraction(G);
Solve(l,mid,G);
solve(mid+1,r,G);
Recover(G);
ps:
后Contraction,先Reduction,这样跑Contraction时就可以少跑一些边了。
其实无所谓。
边界:l==r G中只有一条边,直接改了加,as[i]存第i个操作的答案。
R-C(n) MST时间复杂度 (nlogn)(nlogn的Kruskal的排序,本蒟蒻之前还以为是prim的一些操作orzzzz)
Recover 差不多
总共(n log n^2)-> (2nlogn)
代码
#include<bits/stdc++.h>
#define inf 0x7fffffff
using namespace std;
typedef long long ll;
const int N=5e4+10;
int n,m,Q,cur,f[N],tag[N];
ll as[N],a[N];
struct L{int u,v,id;ll d;}d[N],t[N][20],tp[N];
struct P{int k;ll d;}c[N];
inline int find(int x){return f[x]==0? x:f[x]=find(f[x]);}
inline bool cmp(const L& a,const L& b){return a.d<b.d;}
inline int rd()
{
char ch=getchar();int x=0,f=1;
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
inline void itia(int cnt)
{for(int i=1;i<=cnt;i++) f[d[i].v]=f[d[i].u]=0;}
inline void Reduction(int &cnt)
{
cur=0;itia(cnt);
sort(d+1,d+cnt+1,cmp);
for(int du,dv,i=1;i<=cnt;i++){
du=find(d[i].u);dv=find(d[i].v);
if(du!=dv){
f[du]=dv;
tp[++cur]=d[i];tag[d[i].id]=cur;
}else if(d[i].d==inf) tp[++cur]=d[i],tag[d[i].id]=cur;
}
cnt=cur;
for(int i=1;i<=cnt;i++) d[i]=tp[i];
}
inline void Contraction(int &cnt,ll &must)
{
cur=0;itia(cnt);
sort(d+1,d+cnt+1,cmp);
for(int du,dv,i=1;i<=cnt;i++){
du=find(d[i].u);dv=find(d[i].v);
if(du!=dv) f[du]=dv,tp[++cur]=d[i];
}
for(int i=1;i<=cur;i++) f[tp[i].u]=f[tp[i].v]=0;
for(int du,dv,i=1;i<=cur;i++){
du=find(tp[i].u);dv=find(tp[i].v);
if(tp[i].d!=-inf && du!=dv) f[du]=dv,must+=tp[i].d;
}cur=0;
for(int i=1;i<=cnt;i++){
if(find(d[i].u)!=find(d[i].v)) {
tp[++cur]=d[i];tag[d[i].id]=cur;
tp[cur].u=find(d[i].u),tp[cur].v=find(d[i].v);
}
}
cnt=cur;
for(int i=1;i<=cnt;i++) d[i]=tp[i];
}
inline void cdq(int l,int r,int depth,int cnt,ll must)
{
if(l==r) a[c[l].k]=c[l].d;
for(int i=1;i<=cnt;i++) t[i][depth].d=a[t[i][depth].id];
for(int i=1;i<=cnt;i++)
d[i]=t[i][depth],tag[d[i].id]=i;
//printf("\npay");
//for(int i=1;i<=cnt;i++) printf("%d ",a[t[i][depth].id]);
if(l==r){
as[l]=must;itia(cnt);
sort(d+1,d+cnt+1,cmp);
for(int du,dv,i=1;i<=cnt;i++){
du=find(d[i].u);dv=find(d[i].v);
if(du!=dv) f[du]=dv,as[l]+=d[i].d;
}return;
}
for(int i=l;i<=r;i++) d[tag[c[i].k]].d=inf;
Reduction(cnt);
for(int i=l;i<=r;i++) d[tag[c[i].k]].d=-inf;
Contraction(cnt,must);
for(int i=1;i<=cnt;i++) t[i][depth+1]=d[i];
int mid=(l+r)>>1;
cdq(l,mid,depth+1,cnt,must);cdq(mid+1,r,depth+1,cnt,must);
}
int main(){
n=rd();m=rd();Q=rd();
for(int i=1;i<=m;i++) t[i][0].id=i,t[i][0].u=rd(),t[i][0].v=rd(),t[i][0].d=a[i]=rd();
for(int i=1;i<=Q;i++) c[i].k=rd(),c[i].d=rd();
cdq(1,Q,0,m,0);
for(int i=1;i<=Q;i++) printf("%lld\n",as[i]);
return 0;
}
跑得慢怪我咯QAQ