1. 程式人生 > >BZOJ2001: [Hnoi2010]City 城市建設

BZOJ2001: [Hnoi2010]City 城市建設

BZOJ

題意

給你一張 n n 個點 m m 條邊的無向聯通圖和 Q

Q 個詢問,每次詢問永久性的修改一條邊的邊權,問每次修改後改圖的最小生成樹的權值是多少;

題解

拿到題第一反應這不裸的線段樹分治加個LCT嗎,然後碼碼碼碼碼,碼了150多行交了一發wa,改了一下40,再來一下80,然後T了兩組再也沒漲過;一看題解原來是CDQ分治加神奇的縮點加速(老前輩們的並查集為什麼都是又用啟發式合併又用路徑壓縮);
為什麼說正解是神奇的縮點加速呢,這題是這麼想的,CDQ分治遞迴處理子問題時先把區間裡詢問影響的邊拿出來做 C

o n t r a c t i o n
Contraction - R e d u c t i o n Reduction 操作:先把詢問影響的邊的權值設為 I N F -INF ,然後做一次 M S T MST , M S T MST 上的權值非詢問邊接下來一定會出現在 M S T MST 上(考慮因為詢問邊的權值已經設成了最小,如果詢問邊能代替這些在 M S T MST 上的非詢問邊,那麼一定會先加進去的),所以直接把這些邊的權值累加進答案裡,然後將這些邊構成的聯通塊縮成一個點,多餘的邊就直接刪去只保留詢不在聯通塊裡的邊,接下來再把詢問邊的邊權生成 I N F INF ,再做一次 M S T MST ,只保留 M S T MST 上的邊和詢問邊;在這兩次操作過後邊數點數都大大減少,一直遞迴下去,到了 L = R L=R 時,點數邊數已經非常少了,這個時候只需要永久修改當前詢問邊權,再做一次 M S T MST 便能很快的得到答案;複雜度不會算

線段樹分治+LCT
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5e5+10;
int n,m,Q,top;
int last[N];
struct Edge {
	int u,v,w,id;
}E[N>>1];
vector<Edge> V[N<<2];
struct Data {
	int id,w,type;
	Data () {}
	Data (int a,int b,int c) :id(a),w(b),type(c) {}
}ST[N];
#define L son[rt][0]
#define R son[rt][1]
struct LinkCutTree {
    bool rev[N];
    int son[N][2],fa[N],w[N],ID[N];
    bool Isroot(int rt) {
        return son[fa[rt]][0]!=rt&&son[fa[rt]][1]!=rt;
    }
    bool Side(int rt,int f) {
        return son[f][0]==rt?0:1;
    }
    void Pushup(int rt) {
        ID[rt]=rt;
        if(w[ID[L]]>w[ID[rt]]) ID[rt]=ID[L];
        if(w[ID[R]]>w[ID[rt]]) ID[rt]=ID[R];
	}
    void Pushdown(int rt) {
        if(rev[rt]) {
            swap(L,R);
            rev[L]^=1; rev[R]^=1; rev[rt]^=1;
        }
    }
    void Push(int rt) {
        if(!Isroot(rt)) Push(fa[rt]);
        Pushdown(rt);
    }
    void Rotate(int rt) {
        int f=fa[rt],to=Side(rt,f),ano=son[rt][to^1],fto=Side(f,fa[f]);
        if(!Isroot(f)) son[fa[f]][fto]=rt;
        fa[rt]=fa[f];
        fa[ano]=f; son[f][to]=ano;
        fa[f]=rt; son[rt][to^1]=f;
        Pushup(f); Pushup(rt);
    }
    void Splay(int rt) {
        Push(rt);
        while(!Isroot(rt)) {
            if(Side(rt,fa[rt])==Side(fa[rt],fa[fa[rt]])&&!Isroot(fa[rt])&&!Isroot(fa[fa[rt]])) Rotate(fa[rt]);
            Rotate(rt);
        }
    }
    void Access(int rt) {
        for(int t=0;rt;t=rt,rt=fa[rt]) {
            Splay(rt);
            R=t;
            Pushup(rt);
        }
    }
    void Makeroot(int rt) {
        Access(rt);
        Splay(rt);
        rev[rt]^=1;
    }
    void Split(int x,int y) {
        Makeroot(x);
        Access(y);
        Splay(y);
    }
    void Cut(int x,int y) {
        Split(x,y);
        fa[x]=son[y][0]=0;
        Pushup(y);
    }
    void Link(int x,int y) {
        Makeroot(x);
        fa[x]=y;
    }
    int Find(int rt) {
    	Access(rt);
		Splay(rt);
		while(L) rt=L;
		return rt; 
    }
    int Query(int x,int y) {
    	Split(x,y);
    	return ID[y];
    }
}LCT;
void Modify(int l,int r,int o,int ql,int qr,Edge A) {
	if(ql<=l&&r<=qr) {
		V[o].push_back(A);
		return;
	}
	int mid=l+r>>1;
	if(ql<=mid) Modify(l,mid,o<<1,ql,qr,A);
	if(qr>mid) Modify(mid+1,r,o<<1|1,ql,qr,A);
}
void DAC(int l,int r,int o,LL val) {
	int now=top;
	for(int i=0;i<(int)V[o].size();++i) {
		int u=V[o][i].u,v=V[o][i].v,w=V[o][i].w,id=V[o][i].id;
		int x=LCT.Find(u),y=LCT.Find(v);
		if(x!=y) {
			ST[++top]=Data(id,LCT.w[n+id],0); LCT.w[n+id]=w;
			LCT.Link(u,n+id); LCT.Link(n+id,v);
			val+=w;
		}
		else {
			int t=LCT.Query(u,v);
			if(LCT.w[t]>w) {
				LCT.Cut(E[t-n].u,t); LCT.Cut(t,E[t-n].v);
				ST[++top]=Data(t-n,LCT.w[t],1); val-=LCT.w[t];
				ST[++top]=Data(id,LCT.w[n+id],0); LCT.w[n+id]=w;
				LCT.Link(u,n+id); LCT.Link(n+id,v);
			 	val+=w;
			}
		}
	}
	int mid=l+r>>1;
	if(l==r) {
		if(l>m) printf("%lld\n",val);
	}
	else DAC(l,mid,o<<1,val),DAC(mid+1,r,o<<1|1,val);
	while(top!=now) {
		int t=ST[top].id;
		LCT.w[t+n]=ST[top].w; LCT.ID[t+n]=t+n; 
		if(!ST[top].type) {
			LCT.Cut(E[t].u,t+n); 
			LCT.Cut(t+n,E[t].v);
		}
		else {
			LCT.Link(E[t].u,t+n);
			LCT.Link(t+n,E[t].v);
		}
		--top;
	}
}
int main() {
	scanf("%d%d%d",&n,&m,&Q);
	for(int i=1;i<=m;++i) {
		last[i]=1; E[i].id=i; LCT.ID[i+n]=i+n;
		scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w);
	}
	Q+=m;
	for(int i=m+1;i<=Q;++i) {
		int k,d; scanf("%d%d",&k,&d);
		Modify(1,Q,1,last[k],i-1,E[k]);
		last[k]=i; E[k].w=d;
	}
	for(int i=1;i<=m;++i) Modify(1,Q,1,last[i],Q,E[i]);
	DAC(1,Q,1,0);
	return 0;
}
CDQ分治
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e4+10,M=5e4+10,INF=2e9;
int n,m,Q;
int F[N],W[M],ID[M];
LL ANS[M];
struct Data {
	int id,w;
}A[M];
struct Edge {
	int u,v,w,id;
}E[20][M],G[M],tmp[M];
int Find(int a) {
	return F[a]==a?a:F[a]=Find(F[a]);
}
bool cmp(Edge A,Edge B) {
	return A.w<B.w;
}
void Init(int m)