P4319 變換的道路 題解
阿新 • • 發佈:2022-03-24
很顯然,題目要求每一天的最小生成樹的邊權和 +1。
天數很少,剛開始肯定會想到直接按時間順著推,但其實仔細一想會發現有些邊被棄掉之後想再找回來用是很困難的,所以順著推是肯定不行的。
正解:動態樹+線段樹分治。
這種題一般都是可以對時間(或是詢問等)建一個線段樹,在每一個節點上記這個區間包含的邊,然後利用標記永久化的思想來處理每一個時間點。這樣最後線段樹上會有 \(O(n\log n)\) 條邊。
然後遍歷整個線段樹來得到答案。
具體實現:
每到一個節點時,當這個節點上的邊比現在最小生成樹上的邊更優時,就替換掉它,否則不改變。可以用一個棧來記錄每次操作的加邊和刪邊,這樣回溯的時候可以回退到原來的狀態。因為有動態加邊和刪邊操作,這裡可以用 LCT 來維護最小生成樹。至於如何用 LCT 維護最小生成樹,可以去看看 [WC2006]水管局長和[NOI2014]魔法森林這兩題,都是 LCT 維護最小生成樹的好題。
最後本題的時間複雜度為 \(O(n\log^2n)\)。
程式碼:
#include<cstdio> #include<vector> #define pc(x) putchar(x) #define ll long long #define ls (pos<<1) #define rs (pos<<1|1) using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } void write(ll x) { if(x<0){x=-x;putchar('-');} if(x>9)write(x/10); putchar(x%10+48); } int n,m,top,cnt;ll ans[40000]; struct edge{int u,v;ll w;}e[200005]; struct STK{int id,op;}stk[200005]; int fa[200005],rev[200005],ch[200005][2],mx[200005];ll val[200005]; //LCT begin inline void swap(int &x,int &y){x^=y;y^=x;x^=y;} inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} inline void pushrev(int x){swap(ch[x][0],ch[x][1]);rev[x]^=1;} inline void pushup(int x) { mx[x]=x; if(ch[x][0]&&val[mx[x]]<val[mx[ch[x][0]]])mx[x]=mx[ch[x][0]]; if(ch[x][1]&&val[mx[x]]<val[mx[ch[x][1]]])mx[x]=mx[ch[x][1]]; } inline void pushdown(int x) { if(rev[x]) { if(ch[x][0])pushrev(ch[x][0]); if(ch[x][1])pushrev(ch[x][1]); }rev[x]=0; } inline void update(int x) { if(!isroot(x))update(fa[x]); pushdown(x); } inline void rotate(int x) { int y=fa[x],z=fa[y]; int k=ch[y][1]==x,w=ch[x][k^1]; if(!isroot(y))ch[z][ch[z][1]==y]=x; ch[x][k^1]=y;ch[y][k]=w; if(w){fa[w]=y;}fa[y]=x;fa[x]=z; pushup(y); } inline void splay(int x) { update(x); while(!isroot(x)) { int y=fa[x],z=fa[y]; if(!isroot(y))rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y); rotate(x); }pushup(x); } inline void access(int x) { for(int y=0;x;x=fa[y=x]) {splay(x);ch[x][1]=y;pushup(x);} } inline void makeroot(int x){access(x);splay(x);pushrev(x);} inline void split(int x,int y){makeroot(x);access(y);splay(y);} inline void link(int x,int y){makeroot(x);fa[x]=y;} inline int findroot(int x) { access(x);splay(x); while(ch[x][0])pushdown(x),x=ch[x][0]; splay(x);return x; } inline void cut(int x,int y) { makeroot(x);access(y);splay(x); fa[y]=ch[x][1]=0;pushup(x); } //LCT end ll sum; vector<int>tr[200005]; inline void ret() { int op=stk[top].op,id=stk[top].id; int u=e[id].u,v=e[id].v;top--; if(op==0)cut(u,id),cut(v,id); else link(u,id),link(v,id); } inline void modify(int pos,int l,int r,int L,int R,int id) { if(L<=l&&r<=R){tr[pos].push_back(id);return;} int mid=(l+r)>>1; if(L<=mid)modify(ls,l,mid,L,R,id); if(R>mid)modify(rs,mid+1,r,L,R,id); } inline void solve(int pos,int l,int r) { int nowtop=top;ll nowsum=sum; for(int i=0;i<(int)tr[pos].size();++i) { int id=tr[pos][i],u=e[id].u,v=e[id].v; split(u,v);int mxid=mx[v]; if(val[mxid]<=val[id])continue; stk[++top]=(STK){mxid,1};stk[++top]=(STK){id,0}; cut(e[mxid].u,mxid);cut(e[mxid].v,mxid); link(u,id);link(v,id);sum=sum-val[mxid]+val[id]; }int mid=(l+r)>>1; if(l==r)ans[l]=sum; else solve(ls,l,mid),solve(rs,mid+1,r); sum=nowsum;while(top>nowtop)ret(); } int main() { n=read();cnt=n; for(int i=1;i<n;++i) { int u=read(),v=read(),w=read(); e[++cnt]=(edge){u,v,w};val[cnt]=w; link(u,cnt);link(v,cnt);sum+=w; }m=read(); for(int i=1;i<=m;++i) { int u=read(),v=read(),w=read(),l=read(),r=read(); e[++cnt]=(edge){u,v,w};val[cnt]=w; modify(1,1,32766,l,r,cnt); }solve(1,1,32766); for(int i=1;i<=32766;++i)write(ans[i]+1),pc('\n'); return 0; }