[2016北京集训试题14]股神小D-[LCT]
Description
Solution
将(u,v,l,r)换为(1,u,v,l)和(2,u,v,r)。进行排序(第4个数为第一关键字,第1个数为第二关键字)。用LCT维护联通块的合并和断开。(维护联通块的大小,要维护虚边)
答案统计:每当四元组的第一个数为1(这时候合并点u,v所在连通块,反之拆开),在合并前ans+=size[u]*size[v]即可。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int N=2e5+10; long long ans; struct LCT { int val[N],sz[N],v[N],fa[N],ch[N][2],rev[N]; bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} bool get(int x){return ch[fa[x]][1]==x;} void updata(int x){sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+v[x]+1;} void rotate(int x) { int k=get(x),y=fa[x]; ch[y][k]=ch[x][k^1];fa[ch[y][k]]=y; if (!isroot(y)) ch[fa[y]][ch[fa[y]][1]==y]=x;fa[x]=fa[y]; fa[y]=x;ch[x][k^1]=y; updata(y); updata(x); } void pushdown(int x) { if (rev[x]) { swap(ch[x][0],ch[x][1]); rev[ch[x][0]]^=1;rev[ch[x][1]]^=1; rev[x]=0; } } int q[N],cnt; void splay(int x) { int y; q[cnt=1]=x; for (int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i]; for (int i=cnt;i>=1;i--) pushdown(q[i]),fa[q[i]]=fa[q[i]]; while (!isroot(x)) { y=fa[x]; if (!isroot(y)) rotate(get(x)==get(y)?y:x); rotate(x); } } void access(int x) { int y=0; while (x) { splay(x); v[x]+=sz[ch[x][1]]-sz[y]; ch[x][1]=y; updata(x); y=x;x=fa[x]; } } void mroot(int x) { access(x);splay(x);rev[x]^=1; } void link(int x,int y) { mroot(x);access(y);splay(y); ans+=1ll*sz[x]*sz[y]; fa[x]=y;v[y]+=sz[x];updata(y); } void cut(int x,int y) { mroot(x);access(y);splay(y); fa[x]=ch[y][0]=0; updata(y); } }lct; int n,u,v,l,r; struct Q{int t,u,v,w; friend bool operator <(Q a,Q b){return a.w==b.w?a.t<b.t:a.w<b.w;} }q[N<<1]; int main() { scanf("%d",&n); for (int i=1;i<n;i++) { scanf("%d%d%d%d",&u,&v,&l,&r); q[i*2-1]=Q{1,u,v,l};q[i*2]=Q{2,u,v,r}; } sort(q+1,q+2*n-1); for (int i=1;i<=2*n-2;i++) { if (q[i].t==1) lct.link(q[i].u,q[i].v); else lct.cut(q[i].u,q[i].v); } cout<<ans; }