题意:
无向图,求每次修改一条边权值后的最小生成树的边权和。
解析:
网上题解都是些什么CDQ重构图的鬼畜算法。
wyf大爷提出了用LCT以及分治解决这道题的办法。
整个时间看做一个轴的话。
那么每条边的颜色必然是几段连续的区间。
所以我们可以处理出来每条边在某时间的颜色是什么。
之后参见4025的分治做法。
在递归的时候开栈记录做了什么删边加边操作,只要逆回去就可以了。
其实可以看做是对于时间轴的一棵线段树。
对于每一个叶子节点,其实就是把所有他到根节点路径上的边加进去即可。
回溯再撤销之前的操作即可。
总共的区间个数大概在O(Qloglogn)左右。
虽然这个玩意的复杂度是对的。
但是由于逆操作以及操作时的次数过多,所以常数非常大。
这题即需要继续进一步卡常数。
然而我并没有卡过去。
但是这是个不鬼畜的思路:)
代码:
#include
#include
#include
#include
#include
#define N 71000
#define M 51000
#define len 1<<15
using namespace std;
typedef pair<int,int> pa;
typedef long long ll;
bool isdigit(char ch)
{
return ch>='0'&&ch<='9';
}
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++;
}
int read()
{
static char ch;
static int d;
while(!isdigit(ch=getc()));
for(d=ch-'0';isdigit(ch=getc());)
d=(d<<1)+(d<<3)+ch-'0';
return d;
}
struct Que
{
int st,ed,no;
int val;
Que(){}
Que(int _st,int _ed,int _no,int _val):st(_st),ed(_ed),no(_no),val(_val){}
};
vector query;
struct Line
{
int u,v,no;
int val;
Line(){}
Line(int _u,int _v,int _val,int _no):u(_u),v(_v),val(_val),no(_no){}
}line[M];
int n,m,q;
int ch[N][2],fa[N],maxnode[N];
int val[N],mx[N];
bool is_rt[N],rev[N];
inline void init()
{
memset(is_rt,true,sizeof(is_rt));
}
inline void pushup(int rt)
{
if(!rt)return;
maxnode[rt]=rt;
mx[rt]=val[rt];
if(ch[rt][0]!=0&&mx[ch[rt][0]]>mx[rt])
mx[rt]=mx[ch[rt][0]],maxnode[rt]=maxnode[ch[rt][0]];
if(ch[rt][1]!=0&&mx[ch[rt][1]]>mx[rt])
mx[rt]=mx[ch[rt][1]],maxnode[rt]=maxnode[ch[rt][1]];
}
inline void update_rev(int rt)
{
if(!rt)return;
swap(ch[rt][0],ch[rt][1]);
rev[rt]^=true;
}
inline void pushdown(int rt)
{
if(!rt)return;
if(rev[rt])
{
update_rev(ch[rt][0]),update_rev(ch[rt][1]);
rev[rt]^=true;
}
}
inline void down(int rt)
{
if(!is_rt[rt])down(fa[rt]);
pushdown(rt);
}
inline void rotate(int rt,int kind)
{
int y=fa[rt];
ch[y][kind]=ch[rt][!kind];
fa[ch[y][kind]]=y;
ch[rt][!kind]=y;
fa[rt]=fa[y];
fa[y]=rt;
if(is_rt[y])
is_rt[y]=false,is_rt[rt]=true;
else ch[fa[rt]][ch[fa[rt]][1]==y]=rt;
pushup(y);
}
inline void splay(int rt)
{
down(rt);
while(!is_rt[rt])
{
int y=fa[rt],kind=(ch[y][1]==rt);
if(is_rt[y])rotate(rt,kind);
else if((ch[fa[y]][1]==y)==kind)
rotate(y,kind),rotate(rt,kind);
else rotate(rt,kind),rotate(rt,!kind);
}
pushup(rt);
}
inline void access(int rt)
{
int y=0;
while(rt)
{
splay(rt);
is_rt[ch[rt][1]]=true;
is_rt[y]=false;
ch[rt][1]=y;
pushup(rt);
y=rt,rt=fa[rt];
}
}
inline void movetoroot(int rt){access(rt);splay(rt);update_rev(rt);}
inline void link(int x,int y){movetoroot(x);fa[x]=y;}
inline void cut(int x,int y){movetoroot(x);access(y);splay(y);ch[y][0]=0,fa[x]=0,is_rt[x]=true;}
inline bool findroot(int x,int y){movetoroot(x),access(y),splay(y);while(ch[x][0])x=ch[x][0];while(ch[y][0])y=ch[y][0];return x==y;}
int pre[M];
int preval[M];
ll ans;
struct Stack
{
int no,val;
Stack(){}
Stack(int _no,int _val):no(_no),val(_val){}
}sta[M<<3];
int top;
inline void Restore(int bot)
{
while(top>bot)
{
if(sta[top].no<0)
{
cut(line[-sta[top].no].u,-sta[top].no+n);
cut(-sta[top].no+n,line[-sta[top].no].v);
ans-=sta[top].val;
}else
{
val[sta[top].no+n]=mx[sta[top].no+n]=sta[top].val;
maxnode[sta[top].no+n]=sta[top].no+n;
link(line[sta[top].no].u,sta[top].no+n),link(sta[top].no+n,line[sta[top].no].v);
ans+=sta[top].val;
}
top--;
}
}
inline void Divide_and_Conquer(int L,int R,vector &ve)
{
int mid=(L+R)>>1;
vector ::iterator it;
vector l,r;
int bot=top;
for(it=ve.begin();it!=ve.end();it++)
{
if(it->st==L&&it->ed==R)
{
int x=line[it->no].u,y=line[it->no].v;
if(findroot(x,y))
{
ll tmpval=mx[y];
if(it->val>=tmpval)continue;
else
{
int tmpnode=maxnode[y];
cut(line[tmpnode-n].u,tmpnode),cut(tmpnode,line[tmpnode-n].v);
val[it->no+n]=mx[it->no+n]=it->val;
maxnode[it->no+n]=it->no+n;
link(x,it->no+n),link(it->no+n,y);
ans=ans-tmpval+it->val;
sta[++top]=Stack(tmpnode-n,tmpval);
sta[++top]=Stack(-(it->no),it->val);
}
}else
{
val[it->no+n]=mx[it->no+n]=it->val;
maxnode[it->no+n]=it->no+n;
ans+=it->val;
link(x,it->no+n),link(it->no+n,y);
sta[++top]=Stack(-(it->no),it->val);
}
}else if(it->ed<=mid)
l.push_back(*it);
else if(it->st>mid)
r.push_back(*it);
else l.push_back(Que(it->st,mid,it->no,it->val)),r.push_back(Que(mid+1,it->ed,it->no,it->val));
}
if(L==R)
{
printf("%lld\n",ans);
Restore(bot);
return;
}
Divide_and_Conquer(L,mid,l);
Divide_and_Conquer(mid+1,R,r);
Restore(bot);
}
int main()
{
int i,x,y,z,no,val;
init();
n=read(),m=read(),q=read();
for(i=1;i<=m;i++)
{
x=read(),y=read(),z=read();
line[i]=Line(x,y,z,i);
pre[i]=1,preval[i]=z;
}
for(i=1;i<=q;i++)
{
no=read(),val=read();
int L=pre[no],R=i-1;
if(L>R)pre[no]=i,preval[no]=val;
else
{
query.push_back(Que(L,R,no,preval[no]));
pre[no]=i,preval[no]=val;
}
}
for(int i=1;i<=m;i++)
query.push_back(Que(pre[i],q,i,preval[i]));
Divide_and_Conquer(1,q,query);
}