LCT复习
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复习的更多相关文章
- 省选复习 - LCT 笔记
目录 LCT 笔记 主要功能 和其它数据结构的比较 思想 虚实剖分 如何维护所有的链 实链 虚边 开始构思 具体要维护的功能(从基础到高级) Splay部分 access(u) make(u) fin ...
- 算法复习——LCT(bzoj2049洞穴勘测)
题目: Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连 ...
- 【BZOJ 3669】【NOI 2014】魔法森林 LCT+枚举边
$LCT+枚举$ 复习一下$LCT$模板. 先以$Ai$为关键字$sort$,然后$Ai$从小到大枚举每条边,看能否构成环,构不成则加边,构成则判断,判断过了就切断$Bi$最大的边. 我的边是编号为$ ...
- 从ZOJ2114(Transportation Network)到Link-cut-tree(LCT)
[热烈庆祝ZOJ回归] [首先声明:LCT≠动态树,前者是一种数据结构,而后者是一类问题,即:LCT—解决—>动态树] Link-cut-tree(下文统称LCT)是一种强大的数据结构,不仅可以 ...
- Luogu4546 THUWC2017 在美妙的数学王国中畅游 LCT、泰勒展开
传送门 题意:反正就是一堆操作 LCT总是和玄学东西放在一起我们不妨令$x_0=0.5$(其实取什么都是一样的,但是最好取在$[0,1]$的范围内),将其代入给出的式子,我们得到的$f(x)$的式子就 ...
- 【BZOJ】3282: Tree(lct)
http://www.lydsy.com/JudgeOnline/problem.php?id=3282 复习了下lct,发现两个问题.. 1:一开始我以为splay那里直接全部rot(x)就好了,然 ...
- 模板复习【updating】
马上就要noi了……可能滚粗已经稳了……但是还是要复习模板啊 LCT: bzoj2049 1A 7min # include <stdio.h> # include <string. ...
- 【bzoj4530】大融合(LCT的子树维护)
LCT维护子树并没有想象中的那么难,在这里只是复习下. (其他的维护子树的题目,可见:“共价大爷游长沙”) 只要记录下虚边连接的信息就好了. #include<bits/stdc++.h> ...
- wawawa8的模板复习计划
wawawa8的模板复习计划 数据结构 //手写堆 [link][https://www.luogu.org/problemnew/show/P3378] //并查集 [link][https://w ...
随机推荐
- vim配置C++开发环境 win10
资料一 —— vim插件的安装 https://www.cnblogs.com/tianzhiyi/p/5338032.html 资料二 —— vim多窗口操作: https://blog.csdn. ...
- Is jQuery Still Relevant in 2018?
DOM Selection $('.someclass') document.querySelector('.someclass') document.querySlectorAll('.somecl ...
- 光盘文件的挂载和yum源配置
一.挂载光盘文件 1.将光盘推入 2.新建挂载点 mkdir /mnt/cdrom 3.挂载 3.1临时挂载 mount /dev/dcrom /mnt/cdrom 或者 mount –t iso ...
- 小学生绞尽脑汁也学不会的python(初识面对对象)
小学生绞尽脑汁也学不会的python(初识面对对象) 一. 面向对象思想 1. 面向过程. 重点在"过程". 按照实物的发展流程. 先干嘛,后干嘛, 最后干嘛.... 优点: 简单 ...
- Springboot2本地锁实践
在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交…. 下面我们利用 ...
- 洛谷 P1529 回家 Bessie Come Home
P1529 回家 Bessie Come Home 题目描述 现在是晚餐时间,而母牛们在外面分散的牧场中. 农民约翰按响了电铃,所以她们开始向谷仓走去. 你的工作是要指出哪只母牛会最先到达谷仓(在给出 ...
- Sybase数据库工具DbVisualizer乱码问题
使用DbVisualizer来操作sybase数据库的时候,会出现乱码问题,中文变成 '口口'. 解决的方法例如以下: 将这三个字体都改成 "宋体" 或者改成 "PM ...
- Bmob移动后端云服务平台--Android从零開始--(二)android高速入门
Bmob移动后端云服务平台--Android从零開始--(二)android高速入门 上一篇博文我们简介何为Bmob移动后端服务平台,以及其相关功能和优势. 本文将利用Bmob高速实现简单样例,进一步 ...
- 对Shell几个冷知识的总结(IFS,数组,替换,分割,查找)
IFS: 对IFS的用处直接进行说明,详细IFS是干什么的...自行谷歌 首先创建一个 "a a",和"a"的文件: 然后我们 ls查看一下: --> l ...
- Swift 3.0(一)
一:let 和 var let 声明的是一个常量, var 声明的是一个变量 二:简单数据类型 1.自推出数据类型 let implicitDouble = 70.0 //根据初始化时赋值的数据 ...