妈的,我也是个睿智,当年CDQ在wc讲的时候给了辣么多例题,最后压轴题就是这个。
于是学了CDQ分治的我当天晚上强行写了这个。
题意很简单,在不改变树的状况下,强行修改边权,维护动态最小生成树。
两种做法,都不简单。
考虑对询问进行分治,假设正在处理询问区间 [l,r][l,r] 。
把所有边分为待修改(即在区间 [l,r][l,r] 中存在修改)和确定的。考虑把待修改的边权赋值为INF后来做Kruskal,那么仍不能加入最小生成树的确定边一定不能加入最小生成树,直接删除;考虑把待修改的边权赋值为-INF后来做Kruskal,那么仍能够加入最小生成树的确定边一定在最小生成树上,用并查集缩点。
接下来递归处理 [l,mid][l,mid] 和 [mid+1,r][mid+1,r] 。
可以发现这两种操作后,边数一定不会超过 (r−l+1)×2−1(r−l+1)×2−1 。
T(n)=2×T(n/2)+O(mlogm)T(n)=2×T(n/2)+O(mlogm) ,解得 T(n)=O(mlognlogm)T(n)=O(mlognlogm) 。
1 CDQ分治强行离线
/*
假设G是一个带权无向图的边集,S是G的一个子集。 将S中边权设为+inf后,T是图G的最小生成树。 也可以说T是G-S的MST
定理1:不管S中的边权怎么变化,G的最小生成树T’将属于T∪S。 对于任意一条不属于T∪S的边,我们可以在T中找到链接它两端的一条路径。
由于这些边取值都和S无关,无论S权值怎么更改,这个环上这条边最大,不会进入MST
定理2:在定理1的前提下,我们可以在不影响T’的情况下,将G的边数缩减到n+|s|-1以下。
直接运用定理1,G-S的最小生成树最多有N-1条边。
其他的边不可能在T’中,我们可以安全地删除掉。
这一步被称为Reduction,效果是减少了G的边数。
复杂度同MST是O(mlogm),m=|G|。
定理3:不管S中的边权怎么变化。G的最小生成树T’将包含T-S。
考虑将S的权值一条边一条边提升的情况。
每提升一条边权值,MST要么不变,要么就是S中的一条边离开,一条新边加入。
无论如何,T-S这些边都不会离开MST。
定理4:在定理3的前提下,我们可以在不影响T’的情况下,将G的点数缩减到|s|+1以下。
假设已经对图进行了Reduction。
根据定理3,由于T-S的边不离开MST,我们可以将这些边连接的点合并为联通分量,将联通分量视为节点。
之后根据节点归属更新边表即可。
这一步被称为Contraction,效果是减少了G的点数。
复杂度同MST,O(mlogm)
根据上面的推论,我们可以得到结论:
给定一个图G和操作序列S,可以在O(mlogm)时间内通过reduction-contraction将图的边数缩小到n+|s|-1,点数缩小到|s|+1而保持求解过程的正确性。
*/
#include<bits/stdc++.h>
using namespace std;
inline void read(int &x){
x=0;
int f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
struct edge{
int u,v,w,del,uni,num;
};
vector<edge> e;
int n,m,q;
int cnt=0;
struct Data{
int x,w;
}que[100000];
int fa[100000]={0};
bool operator <(edge a,edge b){
return a.w<b.w;
}
long long ans[300000]={0};
int find(int x){
if(fa[x]==x){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
bool union_n(int x,int y){
int dx=find(x);
int dy=find(y);
if(dx==dy)
return false;
fa[dx]=dy;
return true;
}
void init(int n){
for(int i=0;i<=n;i++){
fa[i]=i;
}
}
void solve(int l,int r,vector<edge> e,int n,long long sum,int newl,int newr)
{
map<int,int> hash;
int mid=(l+r)>>1;
if (l==r) newr=r;
for (int i=0;i<e.size();i++) hash.insert(make_pair(e[i].num,i));
for (int i=newl;i<=newr;i++) //修改边操作
{
Data &q=que[i];
if (hash.count(q.x))
e[hash[q.x]].w=que[i].w;
}
sort(e.begin(),e.end());
if (l==r)//找到答案
{
init(n+10);//为了保险多初始化一点
for (int i=0;i<e.size();i++) if (union_n(e[i].u,e[i].v)) sum+=e[i].w;
ans[l]=sum;
return;
}
set<int> t;//仅仅需要使用l,r之间的边
for (int i=l;i<=r;i++) t.insert(que[i].x);
init(n+10);
for (int i=0;i<e.size();i++)//缩边,将询问中的边设置为INF.有定理这时总边集e中不在生成树里的边,一定不在l,r查询的生成树中
{
if (t.count(e[i].num)) continue;//这一步相当于把该条边的边权定为无穷大,相当于删掉了该边
if (!union_n(e[i].u,e[i].v)) e[i].del=true;
}
init(n+10);
for (int i=0;i<e.size();i++)//缩点,将询问边边权设为无穷小.有定理此时总边集e中在生成树中的边必定在l,r查询的生成树中
if (t.count(e[i].num)) union_n(e[i].u,e[i].v);//删掉一条边合并两个点
for (int i=0;i<e.size();i++)
{
if (t.count(e[i].num)||e[i].del) continue;
if (union_n(e[i].u,e[i].v)) e[i].uni=true,sum+=e[i].w;//这条边一定在生成树中
}
init(n+10);//开始重构图
for (int i=0;i<e.size();i++) if (e[i].uni) union_n(e[i].u,e[i].v);//缩点
map<int,int> mp;
int top=0;
vector<edge> E;
for (int i=1;i<=n;i++)
if (find(i)==i)
mp[i]=++top;
for (int i=0;i<e.size();i++)
{
if (e[i].del||e[i].uni) continue;//不考虑一定在生成树中的和一定不在生成树中的边
edge T=e[i];T.u=mp[find(T.u)];T.v=mp[find(T.v)];
E.push_back(T);
}
solve(l,mid,E,top,sum,l,0);
solve(mid+1,r,E,top,sum,l,mid);
}
//void solve(int l,int r,vector<edge> e,int n,long long sum,int newr,int newl){
// map<int,int> hash;
// int mid=(l+r)/2;
// if(l==r)
// newr=r;
// for(int i=0;i<e.size();i++){
// hash.insert(make_pair(e[i].num,i));
// }
// for(int i=newl;i<=newr;i++){
// Data &q=que[i];
// if(hash.count(q.x)){
// e[hash[q.x]].w=que[i].w;
// }
// }
// sort(e.begin(),e.end());
// if(l==r){
// init(n+20);
// for(int i=0;i<e.size();i++){
// if(union_n(e[i].u,e[i].v)){
// sum+=e[i].w;
// }
// }
// ans[l]=sum;
// return;
// }
// set<int> t;
// for(int i=l;i<=r;i++){
// t.insert(que[i].x);
// }
// init(n+20);
// for(int i=0;i<e.size();i++){
// if(t.count(e[i].num)){
// continue;
// }
// if(!union_n(e[i].u,e[i].v)){
// e[i].del=true;
// }
// }
// init(n+20);
// for(int i=0;i<e.size();i++){
// if(t.count(e[i].num)){
// union_n(e[i].u,e[i].v);
// }
// }
// for(int i=0;i<e.size();i++){
// if(t.count(e[i].num)||e[i].del==true){
// continue;
// }
// if(union_n(e[i].u,e[i].v)){
// e[i].uni=true;
// sum+=e[i].w;
// }
// }
// init(n+10);
// //重新构图了^0^
// for(int i=0;i<e.size();i++){
// if(e[i].uni){
// union_n(e[i].u,e[i].v);//缩去已连接的边。
// }
// }
// map<int,int> mp;
// int top=0;
// vector<edge> E;
// for(int i=1;i<=n;i++){
// if(find(i)==i){
// mp[i]=++top;
// }
// }
// for(int i=0;i<e.size();i++){
// if(e[i].del||e[i].uni)
// continue;
// edge T=e[i];
// T.u=mp[find(T.u)];
// T.v=mp[find(T.v)];
// E.push_back(T);
// }
// solve(l,mid,E,top,sum,l,0);
// solve(mid+1,r,E,top,sum,l,mid);
//}
int main(){
read(n);
read(m);
read(q);
for(int i=1;i<=m;i++){
int u,v,w;
read(u);
read(v);
read(w);
edge T;
T.u=u;
T.v=v;
T.w=w;
T.num=i;
T.del=0;
T.uni=0;
e.push_back(T);
}
for(int i=1;i<=q;i++){
read(que[i].x);
read(que[i].w);
}
solve(1,q,e,n,0,1,0);
for(int i=1;i<=q;i++){
printf("%lld\n",ans[i]);
}
}
2 LCT在线做CDQ
/*LCT*/
#include <bits/stdc++.h>
#define il inline
#define RG register
#define ll long long
#define N (150005)
#define ls (x<<1)
#define rs (x<<1|1)
#define max(a,b) (a>b ? a : b)
#define swap(a,b) (tmp=a,a=b,b=tmp)
#define isroot(x) (ch[fa[x]][0]!=x && ch[fa[x]][1]!=x)
#define makeroot(x) (access(x),splay(x),rev[x]^=1)
#define link(x,y) (makeroot(x),fa[x]=y)
#define cut(x,y) (makeroot(x),access(y),splay(y),ch[y][0]=fa[x]=0,pushup(y))
#define pushdown(x) (rev[x]=0,rev[ch[x][0]]^=1,rev[ch[x][1]]^=1,swap(ch[x][0],ch[x][1]))
#define insert(from,to) (g[++num]=(E){head[from],to},head[from]=num)
using namespace std;
struct data{ int mx,id; }tr[N],pt[N];
struct edge{ int u,v,w,l,r; }e[N],q[N];
struct E{ int nt,to; }g[20*N];
int ch[N][2],fa[N],id[N],mx[N],val[N],rev[N],st[N],Q,n,m;
int head[N],que[N],b[N],t[N],f[N],sz[N],num,tot,end,tmp;
ll ans[N],Ans;
il int gi(){
RG int x=0,q=1; RG char ch=getchar();
while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
if (ch=='-') q=-1,ch=getchar();
while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
return q*x;
}
il void pushup(RG int x){
RG int &l=ch[x][0],&r=ch[x][1]; mx[x]=val[x],id[x]=x;
mx[x]<mx[l]?(mx[x]=mx[l],id[x]=id[l]):0;
mx[x]<mx[r]?(mx[x]=mx[r],id[x]=id[r]):0; return;
}
il void rotate(RG int x){
RG int y=fa[x],z=fa[y],k=ch[y][0]==x,&v=ch[x][k];
!isroot(y)?(ch[z][ch[z][1]==y]=x):0;
fa[x]=z,ch[y][k^1]=v,fa[v]=y;
v=y,fa[y]=x,pushup(y),pushup(x); return;
}
il void splay(RG int x){
RG int top=0; st[++top]=x;
for (RG int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i];
for (RG int i=top;i;--i) if (rev[st[i]]) pushdown(st[i]);
while (!isroot(x)){
RG int y=fa[x],z=fa[y];
if (!isroot(y)) rotate((ch[z][0]==y)^(ch[y][0]==x)?x:y);
rotate(x);
}
return;
}
il void access(RG int x){
RG int t=0;
while (x) splay(x),ch[x][1]=t,pushup(x),t=x,x=fa[x];
return;
}
il int find(RG int x){ while (x!=f[x]) x=f[x]; return x; }
il data query(RG int x,RG int y){
makeroot(x),access(y),splay(y);
return (data){mx[y],id[y]};
}
il void query(RG int x,RG int l,RG int r,RG int k){
if (q[k].l<=l && r<=q[k].r){ insert(x,k); return; }
RG int mid=(l+r)>>1;
if (q[k].r<=mid) query(ls,l,mid,k);
else if (q[k].l>mid) query(rs,mid+1,r,k);
else query(ls,l,mid,k),query(rs,mid+1,r,k);
return;
}
il void add(RG int x){
for (RG int i=head[x],k,x,y;i;i=g[i].nt){
k=g[i].to;
if ((x=find(q[k].u))!=(y=find(q[k].v))){
Ans+=q[k].w,link(q[k].v,k+n),link(k+n,q[k].u);
if (sz[x]>sz[y]) swap(x,y); sz[y]+=sz[x],f[x]=y;
que[++end]=k,b[end]=0,pt[end]=(data){x,y};
}else{
RG data res=query(q[k].v,q[k].u);
if (res.mx<=q[k].w) continue; res.id-=n;
cut(q[res.id].u,res.id+n),cut(res.id+n,q[res.id].v);
link(q[k].u,k+n),link(k+n,q[k].v);
Ans+=q[k].w-res.mx,que[++end]=k,b[end]=res.id;
}
}
return;
}
il void del(RG int top){
RG int id,x,y;
while (end>top){
id=que[end],Ans-=q[id].w,cut(q[id].u,id+n),cut(id+n,q[id].v);
if (!b[end]) x=pt[end].mx,y=pt[end].id,sz[y]-=sz[x],f[x]=x;
else id=b[end],Ans+=q[id].w,link(q[id].u,id+n),link(id+n,q[id].v);
--end;
}
return;
}
il void solve(RG int x,RG int l,RG int r){
RG int tp=end; add(x);
if (l==r) ans[l]=Ans; else{
RG int mid=(l+r)>>1;
solve(ls,l,mid),solve(rs,mid+1,r);
}
del(tp); return;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("city.in","r",stdin);
freopen("city.out","w",stdout);
#endif
n=gi(),m=gi(),Q=gi();
for (RG int i=1;i<=n;++i) f[i]=i,sz[i]=1;
for (RG int i=1;i<=m;++i)
e[i].u=gi(),e[i].v=gi(),e[i].w=gi(),t[i]=1;
for (RG int i=1,k,w;i<=Q;++i){
k=gi(),w=gi(),q[++tot]=e[k],e[k].w=w;
q[tot].l=t[k],q[tot].r=i-1,t[k]=i;
}
for (RG int i=1;i<=m;++i)
q[++tot]=e[i],q[tot].l=t[i],q[tot].r=Q;
for (RG int i=1;i<=tot;++i) val[i+n]=q[i].w;
for (RG int i=1;i<=tot;++i)
if (q[i].l<=q[i].r) query(1,1,Q,i);
solve(1,1,Q);
for (RG int i=1;i<=Q;++i) printf("%lld\n",ans[i]);
return 0;
}