题意:
给出一个n个结点m条边的带权无向连通图,有q次操作;
每次操作是修改一个边的权值,要求每次操作后输出这个图中最小生成树的权值和;
n<=20000,m<=50000,q<=50000;
题解:
网上题解都是那些鬼畜的分治做法,每层求最小生成树将问题缩小到可以接受的范围;
不过那个方法不好理解并且难以推广,所以wyfcyx大爷提出了一种更加让人愉悦的做法;
首先这个问题不能直接用LCT维护,因为当删去一条边(边的权值变大)之后,我们无法知道这两个连通块之间是否还有更小边相连;
也就是说,LCT维护最小生成树不能删边,所以利用分治做一个转化;
我们考虑一个时间轴,每一条边作为一个区间覆盖了它存在的时间段;
那么对于一个询问的时间点,当时在这个图上的边即为覆盖在点上面的那些;
所以用线段树的姿势,将每个边的区间拆成不超过log个,然后挂在那个线段树结点上;
现在从根到某个线段树的叶子,将所有经过结点挂的边一一加入LCT跑最小生成树,跑到最下面就是对那个时间点的答案;
而因为要查询全部,那就应当在深搜回溯的时候,删除刚才插入的边,然后返回父结点,再向另一个子结点搜索;
但是LCT并不能支持删边啊。。。也并不支持向左右子树各自可持久化的分叉出一个版本。。。
然而wyfcyx大爷出现了!因为LCT的操作是十分简单的,只有Link和Cut,并且恰为相反操作;
因此记录每次加入时弹掉的边和加入的边,回溯的时候直接逆操作恢复LCT即可,这样复杂度仍然是有保障的;
虽说如此是有保障的,但是实际操作上非常卡!这个算法相当于每一条边加了两次删了两次,再算上LCT的大常数,所以不太好过嘛;
卡卡常数,时间复杂度O(nlog^2n)就可以在BZ过啦;
最终答案要开long long;
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 71000
#define M 51000
#define MEM 800000
#define LEN 1<<15
#define lson l,mid,no<<1
#define rson mid+1,r,no<<1|1
#define which(x) (ch[fa[x]][1]==x)
using namespace std;
typedef long long ll;
struct edge
{
int x,y,val,no;
}E[M],list[MEM];
ll ans[M],tot;
int last[M];
int head[M<<2],next[MEM],ce;
int fa[N],ch[N][2],val[N],ma[N],n,m;
int st[MEM],stv[MEM],top;
bool opt[MEM];
bool rev[N],rt[N];
inline char getc()
{
static char *S,*T,buf[LEN];
if(S==T)
{
T=(S=buf)+fread(buf,1,LEN,stdin);
if(S==T)
return EOF;
}
return *S++;
}
inline bool isdigit(char x)
{
return '0'<=x&&x<='9';
}
inline int read()
{
static char ch;
static int D;
while(!isdigit(ch=getc()));
for(D=ch-'0';isdigit(ch=getc());)
D=D*10+ch-'0';
return D;
}
inline void Pushup(int x)
{
ma[x]=val[ma[ch[x][0]]]>val[ma[ch[x][1]]]?ma[ch[x][0]]:ma[ch[x][1]];
ma[x]=val[ma[x]]>val[x]?ma[x]:x;
}
inline void Pushdown(int x)
{
if(rev[x])
{
swap(ch[x][0],ch[x][1]);
rev[ch[x][0]]^=1;
rev[ch[x][1]]^=1;
rev[x]=0;
}
}
inline void down(int x)
{
if(!rt[x]) down(fa[x]);
Pushdown(x);
}
inline void Rotate(int x)
{
int f=fa[x];
bool k=which(x);
ch[f][k]=ch[x][!k];
ch[x][!k]=f;
if(rt[f]) rt[f]^=rt[x]^=1;
else ch[fa[f]][which(f)]=x;
fa[ch[f][k]]=f;
fa[x]=fa[f];
fa[f]=x;
Pushup(f);
// Pushup(x);
}
inline void Splay(int x)
{
down(x);
while(!rt[x])
{
int f=fa[x];
if(rt[f])
{
Rotate(x);
break;
}
if(which(x)^which(f))
Rotate(x);
else
Rotate(f);
Rotate(x);
}
Pushup(x);
}
inline void access(int x)
{
int y=0;
while(x)
{
Splay(x);
rt[ch[x][1]]=1;
ch[x][1]=y;
rt[y]=0;
Pushup(x);
y=x,x=fa[x];
}
}
inline void Mtr(int x)
{
access(x);
Splay(x);
swap(ch[x][0],ch[x][1]);
rev[ch[x][0]]^=1;
rev[ch[x][1]]^=1;
}
inline bool judge(int x,int y)
{
Mtr(x);
access(y);
Splay(x);
while(ch[x][1])
x=ch[x][1];
return x==y;
}
inline void Cut(int x)
{
Mtr(x);
static int t;
t=E[x-n].x;
access(t);
Splay(x);
rt[t]=1,fa[t]=0;
ch[x][1]=0;
Pushup(x);
t=E[x-n].y;
access(t);
Splay(x);
rt[t]=1,fa[t]=0;
ch[x][1]=0;
Pushup(x);
}
inline void Link(int x)
{
Mtr(x);
fa[x]=E[x-n].x;
Mtr(x);
fa[x]=E[x-n].y;
}
inline void add(int no,edge &x)
{
list[++ce]=x;
next[ce]=head[no];
head[no]=ce;
}
inline void update(int l,int r,int no,int st,int en,edge &x)
{
if(st<=l&&r<=en)
add(no,x);
else
{
int mid=l+r>>1;
if(en<=mid) update(lson,st,en,x);
else if(st>mid) update(rson,st,en,x);
else update(lson,st,en,x),update(rson,st,en,x);
}
}
inline void slove(int l,int r,int no)
{
int cnt,i,temp;
for(i=head[no],cnt=0;i;i=next[i])
{
temp=0;
if(!judge(list[i].x,list[i].y)||val[temp=ma[list[i].x]]>=list[i].val)
{
if(temp)
{
cnt++,top++;
st[top]=temp;
stv[top]=val[temp];
opt[top]=0;
Cut(temp);
tot-=val[temp];
}
cnt++,top++;
st[top]=list[i].no;
opt[top]=1;
val[list[i].no]=list[i].val;
Link(list[i].no);
tot+=list[i].val;
}
}
if(l==r)
{
ans[l]=tot;
}
else
{
int mid=l+r>>1;
slove(lson);
slove(rson);
}
while(cnt)
{
if(opt[top]==0)
{
val[st[top]]=stv[top];
Link(st[top]);
tot+=val[st[top]];
}
else
{
Cut(st[top]);
tot-=val[st[top]];
}
top--;
cnt--;
}
}
int main()
{
int q,i,j,k,x,y,v;
n=read(),m=read(),q=read();
for(i=1;i<=m;i++)
{
E[i].x=read(),E[i].y=read(),E[i].val=read();
last[i]=1;
E[i].no=i+n;
}
for(i=1;i<=q;i++)
{
x=read(),y=read();
if(last[x]<=i-1)
update(1,q,1,last[x],i-1,E[x]);
last[x]=i,E[x].val=y;
}
for(i=1;i<=m;i++)
{
update(1,q,1,last[i],q,E[i]);
}
for(i=1;i<=n+m;i++)
{
rt[i]=1;
ma[i]=i;
}
slove(1,q,1);
for(i=1;i<=q;i++)
{
printf("%lld\n",ans[i]);
}
return 0;
}