BZOJ2001: [Hnoi2010]City 城市建設
阿新 • • 發佈:2018-11-06
題意
給你一張 個點 條邊的無向聯通圖和 個詢問,每次詢問永久性的修改一條邊的邊權,問每次修改後改圖的最小生成樹的權值是多少;
題解
拿到題第一反應這不裸的線段樹分治加個LCT嗎,然後碼碼碼碼碼,碼了150多行交了一發wa,改了一下40,再來一下80,然後T了兩組再也沒漲過;一看題解原來是CDQ分治加神奇的縮點加速(
老前輩們的並查集為什麼都是又用啟發式合併又用路徑壓縮);
為什麼說正解是神奇的縮點加速呢,這題是這麼想的,CDQ分治遞迴處理子問題時先把區間裡詢問影響的邊拿出來做 - 操作:先把詢問影響的邊的權值設為 ,然後做一次 , 上的權值非詢問邊接下來一定會出現在 上(考慮因為詢問邊的權值已經設成了最小,如果詢問邊能代替這些在 上的非詢問邊,那麼一定會先加進去的),所以直接把這些邊的權值累加進答案裡,然後將這些邊構成的聯通塊縮成一個點,多餘的邊就直接刪去只保留詢不在聯通塊裡的邊,接下來再把詢問邊的邊權生成 ,再做一次 ,只保留 上的邊和詢問邊;在這兩次操作過後邊數點數都大大減少,一直遞迴下去,到了 時,點數邊數已經非常少了,這個時候只需要永久修改當前詢問邊權,再做一次 便能很快的得到答案;複雜度不會算
線段樹分治+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)