1. 程式人生 > >BZOJ 2001 [Hnoi2010]City 城市建設 LCT+分治(未成功卡時卡過)

BZOJ 2001 [Hnoi2010]City 城市建設 LCT+分治(未成功卡時卡過)

題意:
無向圖,求每次修改一條邊權值後的最小生成樹的邊權和。

解析:
網上題解都是些什麼CDQ重構圖的鬼畜演算法。
wyf大爺提出了用LCT以及分治解決這道題的辦法。
整個時間看做一個軸的話。
那麼每條邊的顏色必然是幾段連續的區間。
所以我們可以處理出來每條邊在某時間的顏色是什麼。
之後參見4025的分治做法。
在遞迴的時候開棧記錄做了什麼刪邊加邊操作,只要逆回去就可以了。
其實可以看做是對於時間軸的一棵線段樹。
對於每一個葉子節點,其實就是把所有他到根節點路徑上的邊加進去即可。
回溯再撤銷之前的操作即可。
總共的區間個數大概在O(Qloglogn)左右。
雖然這個玩意的複雜度是對的。
但是由於逆操作以及操作時的次數過多,所以常數非常大。
這題即需要繼續進一步卡常數。
然而我並沒有卡過去。
但是這是個不鬼畜的思路:)

程式碼:

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#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 <Que> 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<Que> &ve) { int mid=(L+R)>>1; vector<Que>::iterator it; vector<Que>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); }