[2016北京集训试题14]股神小D-[LCT]

时间:2022-09-30 23:36:33

Description

[2016北京集训试题14]股神小D-[LCT]

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+;
long long ans;
struct LCT
{
int val[N],sz[N],v[N],fa[N],ch[N][],rev[N];
bool isroot(int x){return ch[fa[x]][]!=x&&ch[fa[x]][]!=x;}
bool get(int x){return ch[fa[x]][]==x;}
void updata(int x){sz[x]=sz[ch[x][]]+sz[ch[x][]]+v[x]+;}
void rotate(int x)
{
int k=get(x),y=fa[x];
ch[y][k]=ch[x][k^];fa[ch[y][k]]=y;
if (!isroot(y)) ch[fa[y]][ch[fa[y]][]==y]=x;fa[x]=fa[y];
fa[y]=x;ch[x][k^]=y;
updata(y);
updata(x);
}
void pushdown(int x)
{
if (rev[x])
{
swap(ch[x][],ch[x][]);
rev[ch[x][]]^=;rev[ch[x][]]^=;
rev[x]=;
}
}
int q[N],cnt;
void splay(int x)
{
int y;
q[cnt=]=x;
for (int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
for (int i=cnt;i>=;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=;
while (x)
{
splay(x);
v[x]+=sz[ch[x][]]-sz[y];
ch[x][]=y;
updata(x);
y=x;x=fa[x];
}
}
void mroot(int x)
{
access(x);splay(x);rev[x]^=;
}
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][]=;
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<<];
int main()
{
scanf("%d",&n);
for (int i=;i<n;i++)
{
scanf("%d%d%d%d",&u,&v,&l,&r);
q[i*-]=Q{,u,v,l};q[i*]=Q{,u,v,r};
}
sort(q+,q+*n-);
for (int i=;i<=*n-;i++)
{
if (q[i].t==) lct.link(q[i].u,q[i].v);
else lct.cut(q[i].u,q[i].v);
}
cout<<ans;
}