LCT,虚实链剖分。支持连边和断边操作。Tarjan制造。

[HNOI2010]弹飞绵羊

当然这题分块可以做,常数小,但是LCT更无脑。

建立一个虚拟的弹飞节点\(n+1\),初始化时对于一个点假如再弹一次就弹飞了,连n+1,否则连弹到的点。维护一个\(size\)查询就直接split找size就行了。修改的时候对应的link,cut就解决了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=300010;
int n,m,a[N],fa[N],ch[N][3],rev[N],size[N],stack[N],k,x,y;
bool nroot(int x){
return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
}
void update(int x){
size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
void rever(int x){
swap(ch[x][1],ch[x][0]);
rev[x]^=1;
}
void pushdown(int x){
if(rev[x]){
if(ch[x][0])rever(ch[x][0]);
if(ch[x][1])rever(ch[x][1]);
rev[x]=0;
}
}
bool son(int x){
return ch[fa[x]][1]==x;
}
void rotate(int x){
int y=fa[x],z=fa[y],a=son(x),b=son(y),c=ch[x][!a];
if(nroot(y))ch[z][b]=x;
fa[x]=z;
if(c)fa[c]=y;
ch[x][!a]=y;
ch[y][a]=c;
fa[y]=x;
update(y);
update(x);
}
void splay(int x){
int a=x,top=0;
stack[++top]=a;
while(nroot(a))stack[++top]=a=fa[a];
while(top)pushdown(stack[top--]);
int y,z;
while(nroot(x)){
y=fa[x];z=fa[y];
if(nroot(y))
if(son(x)==son(y))rotate(y);
else rotate(x);
rotate(x);
}
}
void access(int x){
for(int y=0;x;x=fa[y=x]){
splay(x),ch[x][1]=y,update(x);
}
}
void makeroot(int x){
access(x);
splay(x);
rever(x);
}
int findroot(int x){
access(x);
splay(x);
while(ch[x][0])pushdown(x),x=ch[x][0];
return x;
}
void split(int x,int y){
makeroot(x);
access(y);
splay(y);
}
void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)fa[x]=y;
}
void cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&fa[x]==y&&!ch[x][1]){
fa[x]=ch[y][0]=0;
update(y);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n+1;i++)size[i]=1;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]+i>n)link(i,n+1);
else link(i,a[i]+i);
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d",&k); if(k==1){
scanf("%d",&x);
x+=1;
split(x,n+1);
printf("%d\n",size[n+1]-1);
}
else{
scanf("%d%d",&x,&y);
x+=1;
if(a[x]+x>n)cut(x,n+1);
else cut(x,x+a[x]);
a[x]=y;
if(x+a[x]>n)link(x,n+1);
else link(x,x+a[x]);
}
}
return 0;
}

[SDOI2017]树点涂色

这题并没有用到link,cut。用到了access。我们该怎么求答案呢?记\(sum[i]\)为i点到跟有多少不同的颜色。然后\(sum[x]+sum[y]-2*sum[lca]+1\)就可以得到答案,子树最值?把点的dfn扔到线段树上就行了。怎么维护?因为只会把当前节点到根的颜色改变,自然想到access。我们把相同颜色的节点扔到一个splay中,然后操作一就是一个access了。还是不能维护?我们在access的过程中会断开当前splay的一部分,splay中连上一些点,断开时我们区间加,连接时我们区间减就行了。

#include<cstdio>
#include<algorithm>
#define MN 410001
#define lp (p<<1)
#define rp ((p<<1)|1)
using namespace std; int read_p,read_ca;
inline int read(){
read_p=0;read_ca=getchar();
while(read_ca<'0'||read_ca>'9') read_ca=getchar();
while(read_ca>='0'&&read_ca<='9') read_p=read_p*10+read_ca-48,read_ca=getchar();
return read_p;
}
struct na{int y,ne;}b[MN<<1];
int n,m,o,l[MN],num=0,x,y,fa[MN],ch[MN][2],df[MN],lo[MN],nm=0,v[MN],de[MN],ma[MN],s[MN],pdf[MN],f[MN][20];
bool rt[MN];
inline int max(int a,int b){return a>b?a:b;}
inline void in(int x,int y){b[++num].y=y;b[num].ne=l[x];l[x]=num;} void rot(int k){
int p=fa[k],bo=ch[p][1]==k;
ch[p][bo]=ch[k][!bo];
ch[k][!bo]=p;
fa[ch[p][bo]]=p;
fa[k]=fa[p];
fa[p]=k;
if (rt[p]) rt[p]=0,rt[k]=1;else ch[fa[k]][ch[fa[k]][1]==p]=k;
p=k;
} void splay(int x){
while (!rt[x]){
if (rt[fa[x]]) rot(x);else
if ((ch[fa[fa[x]]][1]==fa[x])==(ch[fa[x]][1]==x)) rot(fa[x]),rot(x);else rot(x),rot(x);
}
} void add(int p,int l,int r,int L,int R,int v){
if (l==L&&r==R) s[p]+=v,ma[p]+=v;else{
int mid=l+r>>1;
if (R<=mid) add(lp,l,mid,L,R,v);else
if (L>mid) add(rp,mid+1,r,L,R,v);else
add(lp,l,mid,L,mid,v),add(rp,mid+1,r,mid+1,R,v);
ma[p]=max(ma[lp],ma[rp])+s[p];
}
} int ask(int p,int l,int r,int k){
if (l==r) return ma[p];
int mid=l+r>>1;
if (k<=mid) return ask(lp,l,mid,k)+s[p];else return ask(rp,mid+1,r,k)+s[p];
} int ask(int p,int l,int r,int L,int R){
if (L==l&&R==r) return ma[p];
int mid=l+r>>1;
if (R<=mid) return ask(lp,l,mid,L,R)+s[p];else
if (L>mid) return ask(rp,mid+1,r,L,R)+s[p];else
return max(ask(lp,l,mid,L,mid),ask(rp,mid+1,r,mid+1,R))+s[p];
} int lf(int x){return ch[x][0]?lf(ch[x][0]):x;} void acc(int x){
for (int i=0;x;x=fa[i=x]){
splay(x);
if (ch[x][1]) add(1,1,nm,df[lf(ch[x][1])],lo[lf(ch[x][1])],1),rt[ch[x][1]]=1;
rt[ch[x][1]=i]=0;if (ch[x][1]) add(1,1,nm,df[lf(ch[x][1])],lo[lf(ch[x][1])],-1);
}
} void dfs(int x){
f[x][0]=fa[x];
for (int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1];
df[x]=++nm;rt[x]=1;de[x]=de[fa[x]]+1;pdf[nm]=de[x];
for (int i=l[x];i;i=b[i].ne)
if (b[i].y!=fa[x]) fa[b[i].y]=x,dfs(b[i].y);
lo[x]=nm;
} void build(int p,int l,int r){
if (l==r) {ma[p]=pdf[l];return;}
int mid=l+r>>1;
build(lp,l,mid);build(rp,mid+1,r);
ma[p]=max(ma[lp],ma[rp]);
} int lca(int x,int y){
if (de[x]<de[y]) swap(x,y);
for (int i=19;i>=0;i--)
if (f[x][i]&&de[f[x][i]]>=de[y]) x=f[x][i];
if (x==y) return x;
for (int i=19;i>=0;i--)
if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
} int main(){
register int i;
n=read();m=read();
for (i=1;i<n;i++) x=read(),y=read(),in(x,y),in(y,x);
dfs(1);
build(1,1,n);
while (m--){
o=read();
if (o==1) acc(read());else
if (o==2) x=read(),y=read(),o=lca(x,y),printf("%d\n",ask(1,1,n,df[x])+ask(1,1,n,df[y])-2*ask(1,1,n,df[o])+1);else
x=read(),printf("%d\n",ask(1,1,n,df[x],lo[x]));
}
}

[WC2006]水管局长

LCT维护动态图两点最长边。

首先有一个问题,如何用LCT维护边的信息?把边看做一个点,连接两个点时,用这两个点分别连接这一条边就行了。

此题,时间倒流,先把不删的边都建好,有环怎么办?我们找到x,y路径上最长的一条边如果比当前边大,删去,在把当前边连上去。否则不连当前边。找最长边可以用如下代码实现。

void update(int now){
int l=ch[now][0];
int r=ch[now][1];
mxid[now]=now;
if(v[mxid[l]]>v[mxid[now]])mxid[now]=mxid[l];
if(v[mxid[r]]>v[mxid[now]])mxid[now]=mxid[r];
}

然后split之后直接找根的mxid就行了。

不删的边建好后,时光倒流一个一个加边就行了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=300001;
int n,m,q,w[2000][2000],f[N],a[N],b[N],book[2000][2000],ans[N],hhh;
int mxid[N],ch[N][2],v[N],fa[N],rev[N],tot,stack[N];
struct ques{
int u,v,type;
}qu[N];
struct edge{
int u,v,w;
}e[N];
void update(int now){
int l=ch[now][0];
int r=ch[now][1];
mxid[now]=now;
if(v[mxid[l]]>v[mxid[now]])mxid[now]=mxid[l];
if(v[mxid[r]]>v[mxid[now]])mxid[now]=mxid[r];
}
bool isroot(int now){
return ch[fa[now]][1]!=now&&ch[fa[now]][0]!=now;
}
bool son(int now){
return ch[fa[now]][1]==now;
}
void rotate(int x){
int y=fa[x],z=fa[y],a=son(x),b=son(y),c=ch[x][!a];
if(!isroot(y))ch[z][b]=x;fa[x]=z;
if(c)fa[c]=y;ch[y][a]=c;
ch[x][!a]=y;fa[y]=x;
update(y);update(x);
}
void rever(int now){
swap(ch[now][1],ch[now][0]);
rev[now]^=1;
}
void pushdown(int now){
if(rev[now]){
if(ch[now][1])rever(ch[now][1]);
if(ch[now][0])rever(ch[now][0]);
}
rev[now]=0;
}
void splay(int x){
int a=x,top=1;stack[top]=x;
while(!isroot(a))stack[++top]=a=fa[a];
while(top)pushdown(stack[top--]);
while(!isroot(x)){
int y=fa[x],z=fa[y];
if(!isroot(y)){
if(son(x)==son(y))rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x){
for(int y=0;x;x=fa[y=x])
splay(x),ch[x][1]=y,update(x);
}
void makeroot(int x){
access(x);
splay(x);
rever(x);
}
void split(int x,int y){
makeroot(x);
access(y);
splay(y);
}
int findroot(int x){
access(x);
splay(x);
pushdown(x);
while(ch[x][0])pushdown(x=ch[x][0]);
return x;
}
void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)
fa[x]=y;
}
void cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&fa[x]==y&&!ch[x][1])
ch[y][0]=fa[x]=0,update(y);
}
int find(int x){
if(f[x]==x)return x;
else return f[x]=find(f[x]);
}
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
bool cmp(edge a,edge b){
return a.w<b.w;
}
int main(){
n=read();m=read();q=read();
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<=m;i++)
e[i].u=read(),e[i].v=read(),e[i].w=read(),
w[e[i].u][e[i].v]=w[e[i].v][e[i].u]=e[i].w;
sort(e+1,e+1+m,cmp);
for(int i=1;i<=q;i++){
qu[i].type=read(),qu[i].u=read(),qu[i].v=read();
if(qu[i].type==2)book[qu[i].u][qu[i].v]=book[qu[i].v][qu[i].u]=1;
}
tot=n;
for(int i=1;i<=m;i++){
int u=e[i].u,V=e[i].v;
if(book[u][V]||book[V][u])continue;
int fu=find(u),fv=find(V);
if(fu!=fv){
tot++;
a[tot]=u;b[tot]=V;
v[tot]=e[i].w;
link(tot,a[tot]);
link(tot,b[tot]);
f[fu]=fv;
}
}
for(int i=q;i>=1;i--){
if(qu[i].type==2){
int u=qu[i].u,V=qu[i].v;
int fu=find(u),fv=find(V);
if(fu==fv){
split(u,V);
int mx=mxid[V];
if(v[mx]>w[qu[i].u][qu[i].v]){
cut(a[mx],mx);
cut(b[mx],mx);
}
else continue;
}
f[fu]=fv;
tot++;v[tot]=w[qu[i].u][qu[i].v];
a[tot]=qu[i].u;b[tot]=qu[i].v;
link(tot,a[tot]); link(tot,b[tot]);
}
else{
split(qu[i].u,qu[i].v);
ans[++hhh]=v[mxid[qu[i].v]];
}
}
for(int i=hhh;i>=1;i--)printf("%d\n",ans[i]);
return 0;
}

[ZJOI2016]大森林

获得新技能——虚点。我反正是想不出来。单点询问,区间操作的时候可以离线。像差分一样把区间分成两个点。一个插入一个删除。发现还是不好做,就要用到虚点了。预处理时每一个更换生长节点的操作我们建一个虚点接在上一个虚点上。我们跟据操作的时间顺序,每一次生长操作把生长出来的节点挂在最近的一个虚点上。我们离线扫整个区间的时候,扫到一个更改生长点的操作。就把对应的虚点挂在生长点上,到区间末尾时,在把虚点挂回原来的位置。这个过程中处理询问就行了。有一些问题,虚点这么多挂在树上不会影响询问吗?不会,我们把虚点的大小都设为0。把一个区间的生长出来的点挂在不交区间的虚点上不会错吗?不会,因为,当没有扫到虚点对应区间时,虚点回挂在上一个虚点上,又因为虚点大小为0,等价虚点不存在。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=500010;
int n,m,v[N],ch[N][3],sum[N],fa[N],size[N],now,ans[N],tot,num,nu,last,cnt,l[N],r[N],id[N];
bool isroot(int x){
return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
}
void update(int x){
sum[x]=v[x]+sum[ch[x][1]]+sum[ch[x][0]];
}
bool son(int x){
return ch[fa[x]][1]==x;
}
void rotate(int x){
int y=fa[x],z=fa[y],a=son(x),b=son(y),c=ch[x][!a];
if(isroot(y))ch[z][b]=x;
fa[x]=z;
if(c)fa[c]=y;
ch[x][!a]=y;ch[y][a]=c;fa[y]=x;
update(y);update(x);
}
void splay(int x){
while(isroot(x)){
int y=fa[x];int z=fa[y];
if(isroot(y))
if(son(x)==son(y))rotate(y);
else rotate(x);
rotate(x);
}
}
int access(int x){
int y=0;
for(;x;y=x,x=fa[x]){
splay(x);ch[x][1]=y;update(x);
}
return y;
}
void link(int x,int y){
splay(x);
fa[x]=y;
}
void cut(int x){
access(x);
splay(x);
fa[ch[x][0]]=0;ch[x][0]=0;
update(x);
}
int lca(int x,int y){
int tmp=0;
access(x);splay(x);tmp+=sum[x];
int t=access(y);splay(y);tmp+=sum[y];
access(t);splay(t);tmp-=2*sum[t];
return tmp;
}
struct ask{
int k,num,x,y;
friend bool operator <(const ask&a,const ask&b){
return a.k<b.k||(a.k==b.k&&a.num<b.num);
}
}q[N];
void newnode(int x){
v[++tot]=x;
sum[tot]=x;
}
int main(){
scanf("%d%d",&n,&m);
newnode(1);now=1;l[1]=1;r[1]=n;id[1]=1;
newnode(0);last=2;link(2,1);
for(int i=1;i<=m;i++){
int k,a,b,L,R,x;
scanf("%d",&k);
if(k==0){
scanf("%d%d",&a,&b);
newnode(1);
l[++now]=a;r[now]=b;id[now]=tot;
q[++cnt].k=1;q[cnt].num=i-m;q[cnt].x=tot;q[cnt].y=last;
}
else if(k==1){
scanf("%d%d%d",&L,&R,&x);
L=max(L,l[x]);R=min(R,r[x]);
if(L<=R){
newnode(0);link(tot,last);
q[++cnt].k=L;q[cnt].num=i-m;q[cnt].x=tot;q[cnt].y=id[x];
q[++cnt].k=R+1;q[cnt].num=i-m;q[cnt].x=tot;q[cnt].y=last;
last=tot;
}
}
else{
scanf("%d%d%d",&x,&a,&b);
q[++cnt].k=x;q[cnt].num=++nu;q[cnt].x=id[a];q[cnt].y=id[b];
}
}
sort(q+1,q+cnt+1);
for(int i=1,j=1;i<=n;i++){
for(;j<=cnt&&q[j].k==i;j++){
if(q[j].num<=0){
cut(q[j].x);
link(q[j].x,q[j].y);
}
else ans[q[j].num]=lca(q[j].x,q[j].y);
}
}
for(int i=1;i<=nu;i++)printf("%d\n",ans[i]);
return 0;
}

LCT复习的更多相关文章

  1. 省选复习 - LCT 笔记

    目录 LCT 笔记 主要功能 和其它数据结构的比较 思想 虚实剖分 如何维护所有的链 实链 虚边 开始构思 具体要维护的功能(从基础到高级) Splay部分 access(u) make(u) fin ...

  2. 算法复习——LCT(bzoj2049洞穴勘测)

    题目: Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连 ...

  3. 【BZOJ 3669】【NOI 2014】魔法森林 LCT+枚举边

    $LCT+枚举$ 复习一下$LCT$模板. 先以$Ai$为关键字$sort$,然后$Ai$从小到大枚举每条边,看能否构成环,构不成则加边,构成则判断,判断过了就切断$Bi$最大的边. 我的边是编号为$ ...

  4. 从ZOJ2114(Transportation Network)到Link-cut-tree(LCT)

    [热烈庆祝ZOJ回归] [首先声明:LCT≠动态树,前者是一种数据结构,而后者是一类问题,即:LCT—解决—>动态树] Link-cut-tree(下文统称LCT)是一种强大的数据结构,不仅可以 ...

  5. Luogu4546 THUWC2017 在美妙的数学王国中畅游 LCT、泰勒展开

    传送门 题意:反正就是一堆操作 LCT总是和玄学东西放在一起我们不妨令$x_0=0.5$(其实取什么都是一样的,但是最好取在$[0,1]$的范围内),将其代入给出的式子,我们得到的$f(x)$的式子就 ...

  6. 【BZOJ】3282: Tree(lct)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3282 复习了下lct,发现两个问题.. 1:一开始我以为splay那里直接全部rot(x)就好了,然 ...

  7. 模板复习【updating】

    马上就要noi了……可能滚粗已经稳了……但是还是要复习模板啊 LCT: bzoj2049 1A 7min # include <stdio.h> # include <string. ...

  8. 【bzoj4530】大融合(LCT的子树维护)

    LCT维护子树并没有想象中的那么难,在这里只是复习下. (其他的维护子树的题目,可见:“共价大爷游长沙”) 只要记录下虚边连接的信息就好了. #include<bits/stdc++.h> ...

  9. wawawa8的模板复习计划

    wawawa8的模板复习计划 数据结构 //手写堆 [link][https://www.luogu.org/problemnew/show/P3378] //并查集 [link][https://w ...

随机推荐

  1. 使用RestTemplate上传文件给远程接口

    MultiValueMap request = new LinkedMultiValueMap(1); ByteArrayResource is = new ByteArrayResource(mul ...

  2. 面试题sql

    查询书的价格10到20 之前显示10to20 没有显示unknown CREATE TABLE book(price INT,NAME VARCHAR(20)) SELECT NAME AS '名字' ...

  3. gcp – 源于CP的高级命令行文件拷贝工具

    作者:linux 出处:http://linux.cn/thread/11868/1/1/ gcp – 源于CP的高级命令行文件拷贝工具 几周前,我们讨论了高级拷贝(修改于cp命令,让其可以显示复制进 ...

  4. linux下安装Tomcat和java jdk

    1:上传jdk的gz文件到linux’系统 2:解压缩这个文件 tar –zxvf  文件名.tar.gz –C  指定目录 比如我这里解压到 /root/apps/jdk tar –zxvf  jd ...

  5. JSp获取到当前用户的全部session

    <%@page import="java.util.Enumeration"%> <% for (Enumeration<?> e = session ...

  6. 样本方差的无偏估计与(n-1)的由来

    一.无偏估计 所谓总体参数估计量的无偏性指的是,基于不同的样本,使用该估计量可算出多个估计值,但它们的平均值等于被估参数的真值. 在某些场合下,无偏性的要求是有实际意义的.例如,假设在某厂商与某销售商 ...

  7. 命令行 对MYSQL导入sql

    1 use database name;  //选择使用的数据库 2 mysql>source d:\datafilename.sql  导入sql

  8. .conf、.bak是什么格式

    1..conf 是config的简写,也就是配置文件,多用于存取硬件驱动程序的安装配置信息.内容一般是一些硬件的版本号呀,支持什么样的系统等信息.本质上来说就是TXT文件,里面的格式没有统一标准,各个 ...

  9. 51 nod 1431 快乐排队

    1431 快乐排队 题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题  收藏  关注 有一群人在排队,如果某个人想排到前面去,可以花 ...

  10. MVC传递数据-传递对象或对象集合

    前言 本文主要介绍从View(或者js)文件向Controller提交对象或者对象集合.比方.将表格中的一行数据作为一个对象提交.或将多行数据作为一个集合提交到Controller. 回想 从View ...