NKOJ 2936 (BZOJ 2001)城市建设(CDQ分治+LCT)
2018-02-22 22:29
127 查看
P2936【FJ Training 2014 Day2】城市建设
问题描述PS国是一个拥有诸多城市的大国,国王Louis为城市的交通建设可谓绞尽脑汁。Louis可以在某些城市之间修建道路,在不同的城市之间修建道路需要不同的花费。Louis希望建造最少的道路使得国内所有的城市连通。但是由于某些因素,城市之间修建道路需要的花费会随着时间而改变,Louis会不断得到某道路的修建代价改变的消息,他希望每得到一条消息后能立即知道使城市连通的最小花费总和,Louis决定求助于你来完成这个任务。
因版权问题,题目已隐藏。如有需要请私下联系root或nodgd。
输入格式
第一行包含三个整数N,M,Q,分别表示城市的数目,可以修建的道路个数,及收到的消息个数。
接下来有M行,第i+1行有三个用空格隔开的整数Xi,Yi,Zi(1<=Xi,Yi<=N, 0<=Zi<=5*107),表示在城市Xi与城市Yi之间修建道路的代价为Zi。接下来Q行,每行包含两个数k,d,表示输入的第k个道路的修建代价修改为d(即将Zi修改为d)。
输出格式
包含Q行,第i行输出得知前i条消息后使城市连通的最小花费总和。
样例输入
5 5 3
1 2 1
2 3 2
3 4 3
4 5 4
5 1 5
1 6
1 1
5 3
样例输出
14
10
9
提示
对于20%的数据, n≤1000,m≤6000,Q≤6000。
另有20%的数据,n≤1000,m≤50000,Q≤8000,修改后的代价不会比之前的代价低。
对于100%的数据, n≤20000,m≤50000,Q≤50000。
题目让维护一个边权不断变化的动态最小生成树。容易发现修改边权相当于删除一条边再添加一条边。
容易发现LCT可以轻松维护加边操作,但无法维护删边操作。
此时考虑用CDQ分治去掉删边操作。
预处理每条边存在的时间,按时间分治,每次将覆盖了整个左区间或右区间的边插入到左区间或右区间的LCT中。分治底层就是每一个时刻的答案LCT。
但是并不能开nlognnlogn个LCT,空间承受不了,容易发现LCT上的操作是可以撤销的,LINK和CUT互为逆操作,因此只需要用栈记录一下操作,回溯的时候撤销就行。这样就只需要开一颗全局LCT。
最终时间复杂度O(2mlogqlog(2n))O(2mlogqlog(2n)),加上LCT的大常数,导致这样做非常地卡常,想要通过此题需要优秀的常数。
另外本题有另一个利用MST性质的做法,大致是利用MST将边分成三类同时缩点,不断缩小边集和点集。时间复杂度一样,但常数小。
[b]代码(常数巨大以至于不能AC):
#include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #define N 800005 #define ll long long using namespace std; struct node{int x,y,z,id;}E ,LE ,RE ; int n,m,q,L ,R ,TOT,tot,Hash ,la ; int ls ,rs ,fa ,Max ,id ,v ,rev ,S ,top; ll ans,Ans ,Atop; node AS ; inline char getc() { static char *SS,*TT,buf ; if(SS==TT) { TT=(SS=buf)+fread(buf,1,N,stdin); if(SS==TT)return EOF; } return *SS++; } inline bool isdigit(char x) {return '0'<=x&&x<='9';} inline int read() { static char ch; static int D; while(!isdigit(ch=getc())); for(D=ch-'0';isdigit(ch=getc());)D=D*10+ch-'0'; return D; } int GM(int x,int y,int z) { if(x>=y&&x>=z)return x; if(y>=z)return y; return z; } bool Isroot(int x) {return ls[fa[x]]!=x&&rs[fa[x]]!=x;} void MT(int p) { Max[p]=GM(v[p],Max[ls[p]],Max[rs[p]]); if(Max[p]==v[p])id[p]=p; else if(Max[p]==Max[ls[p]])id[p]=id[ls[p]]; else id[p]=id[rs[p]]; } void PD(int p) { if(rev[p]) { swap(ls[p],rs[p]); rev[ls[p]]^=1; rev[rs[p]]^=1; rev[p]^=1; } } void Zig(int x) { int y=fa[x],z=fa[y]; if(!Isroot(y))y==ls[z]?ls[z]=x:rs[z]=x;fa[x]=z; ls[y]=rs[x];fa[rs[x]]=y; rs[x]=y;fa[y]=x; MT(y);MT(x); } void Zag(int x) { int y=fa[x],z=fa[y]; if(!Isroot(y))y==ls[z]?ls[z]=x:rs[z]=x;fa[x]=z; rs[y]=ls[x];fa[ls[x]]=y; ls[x]=y;fa[y]=x; MT(y);MT(x); } void Splay(int x) { int i,y,z; S[++top]=x; for(i=x;!Isroot(i);i=fa[i])S[++top]=fa[i]; while(top)PD(S[top--]); while(!Isroot(x)) { y=fa[x];z=fa[y]; if(!Isroot(y)) { if(y==ls[z])x==ls[y]?(Zig(y),Zig(x)):(Zag(x),Zig(x)); else x==rs[y]?(Zag(y),Zag(x)):(Zig(x),Zag(x)); } else x==ls[y]?Zig(x):Zag(x); } } void Access(int x) { for(int t=0;x;x=fa[x]) { Splay(x); rs[x]=t; MT(x);t=x; } } void Makeroot(int x) { Access(x); Splay(x); rev[x]^=1; } int Findroot(int x) { Access(x); Splay(x); while(ls[x])x=ls[x]; return x; } void Link(int x,int y) { Makeroot(x); fa[x]=y; } void Cut(int x,int y) { Makeroot(x); Access(y); Splay(y); ls[y]=fa[x]=0; } void Insert(node &p,int ty) { int x=p.x,y=p.y,t,d; if(Findroot(x)!=Findroot(y)) { Link(x,p.id); Link(y,p.id); if(ty)AS[++Atop]=(node){x,p.id,1,0},AS[++Atop]=(node){y,p.id,1,0}; ans+=p.z; } else { Makeroot(x); Access(y); Splay(y); if(Max[y]>p.z) { t=id[y]; d=Hash[t]; Cut(t,E[d].x); Cut(t,E[d].y); ans-=v[t]; if(ty)AS[++Atop]=(node){t,E[d].x,2,0},AS[++Atop]=(node){t,E[d].y,2,0}; Link(x,p.id); Link(y,p.id); ans+=p.z; if(ty)AS[++Atop]=(node){x,p.id,1,0},AS[++Atop]=(node){y,p.id,1,0}; } } } void Recover(int T) { while(Atop!=T) { if(AS[Atop].z==1)Cut(AS[Atop].x,AS[Atop].y); else Link(AS[Atop].x,AS[Atop].y); Atop--; } } void CDQ(int l,int r) { int i,j,k,x,y,las=Atop; ll pans=ans; if(l==r){Ans[l]=ans;return;} int mid=l+r>>1; for(i=mid+1;i<=r;i++)if(L[i]<=l)Insert(LE[i],1); CDQ(l,mid);Recover(las);ans=pans; for(i=l;i<=mid+1;i++)if(R[i]>r)Insert(RE[i],1); CDQ(mid+1,r);Recover(las);ans=pans; } int main() { int i,j,k,x,y,z; n=read();m=read();q=read();tot=n; for(i=1;i<=m;i++) { x=read();y=read();z=read(); E[++TOT]=(node){x,y,z,0}; la[TOT]=0; } for(i=1;i<=q;i++) { x=read();y=read(); L[i]=la[x];la[x]=i; LE[i]=E[x];E[x].z=y; R[L[i]]=i;RE[L[i]]=LE[i]; LE[i].id=++tot;v[tot]=LE[i].z;Hash[tot]=x; RE[L[i]].id=++tot;v[tot]=LE[i].z;Hash[tot]=x; } for(i=1;i<=TOT;i++) { k=la[i]; if(k==0||k==1)E[i].id=++tot,v[tot]=E[i].z,Hash[tot]=i,Insert(E[i],0); else R[k]=q+1,RE[k]=E[i],RE[k].id=++tot,v[tot]=RE[k].z,Hash[tot]=i; } CDQ(1,q); for(i=1;i<=q;i++)printf("%lld\n",Ans[i]); }
相关文章推荐
- [动态MST] [CDQ分治] BZOJ2001: [Hnoi2010]City 城市建设
- 【BZOJ】2001 [Hnoi2010]City 城市建设 cdq分治——动态最小生成树
- BZOJ 2001 Hnoi2010 城市建设 分治+LCT
- [bzoj2001][Hnoi2010][City 城市建设] (cdq分治)
- BZOJ2001 [Hnoi2010]City 城市建设 【CDQ分治 + kruskal】
- [动态最小生成树 CDQ分治 Kruscal] BZOJ 2001 [Hnoi2010]City 城市建设
- BZOJ2001 [Hnoi2010]City 城市建设 CDQ分治
- BZOJ 2001 [Hnoi2010]City 城市建设 LCT+分治(未成功卡时卡过)
- BZOJ 4025|二分图|CDQ分治|并查集|LCT
- BZOJ 2001([Hnoi2010]City 城市建设-CDQ重构图-动态最小生成树)
- [BZOJ2001][HNOI2010]City城市建设-CDQ分治
- bzoj 2001: City 城市建设 cdq
- BZOJ 2001 City城市建设 (CDQ分治 + 并查集)
- [CDQ分治 并查集 || LCT] BZOJ 4025 二分图
- BZOJ 2001([Hnoi2010]City 城市建设-CDQ重构图-动态最小生成树)
- bzoj4430 [Nwerc2015]Guessing Camels赌骆驼(CDQ分治)
- BZOJ.2716.[Violet3]天使玩偶(CDQ分治 坐标变换)
- 【Bzoj 3295】 动态逆序对(树套树|CDQ分治)
- BZOJ1176【CDQ分治】【树状数组】
- 【BZOJ2773】ispiti【CDQ分治】【线段树】