Codeforces 13 E Holes 题解(LCT)

题目:CF 13 E.
题目大意:给定一个长度为 n n n的序列 a [ i ] a[i] a[i],表示到了点 i i i后会往后跳 a [ i ] a[i] a[i],即跳到 i + a [ i ] i+a[i] i+a[i],若 i + a [ i ] > n i+a[i]>n i+a[i]>n则是跳出去了.现在给定两种操作:
0.格式 0   i   b 0\,i\,b 0ib,表示把 a [ i ] a[i] a[i]变成b.
1.格式 1   i 1\,i 1i,表示询问从第 i i i个点开始跳,跳出去前跳到的最后一个点和总共跳了几步.
设操作次数为 m m m,则 1 ≤ n , m ≤ 1 0 5 1\leq n,m\leq 10^5 1n,m105.

弱化版:[Hnoi2010]Bounce 弹飞绵羊.

很容易想到把这个模型建成一棵树,并搞出一个虚点 n + 1 n+1 n+1作为根,然后每个点 i i i i + a [ i ] i+a[i] i+a[i]连一条边,若 i + a [ i ] > n i+a[i]>n i+a[i]>n则连向 n + 1 n+1 n+1,于是我们把这道题变成了一道动态树的题.

那么改变 a [ i ] a[i] a[i]其实就变成了改父亲的操作,而询问操作也就可以相应变成动态树上一些信息的查询,可用LCT维护.

首先总共跳了几步很容易,就是点 i i i的深度 − 1 -1 1.而跳出最后一个点就是 i i i到根这条链上深度第二小的点,也就是LCT把链分裂出来时按照深度为键值时 n + 1 n+1 n+1的前驱.

但是我们并不能直接在这棵对应的splay上找前驱,所以想到深度越小则编号越大,直接在LCT上维护编号最大即可,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000;
int cn,tmp[N+9],tt;
int n,m,a[N+9];

struct tree{
  int fa,s[2],siz,rev,max;
}tr[N+9];

bool Isroot(int x){return tr[tr[x].fa].s[0]^x&&tr[tr[x].fa].s[1]^x;}

void Pushup(int x){
  int ls=tr[x].s[0],rs=tr[x].s[1];
  tr[x].siz=tr[ls].siz+tr[rs].siz+1;
  tr[x].max=max(x,max(tr[ls].max,tr[rs].max));
}

void Update_rev(int x){tr[x].rev^=1;swap(tr[x].s[0],tr[x].s[1]);}

void Pushdown(int x){
  if (!tr[x].rev) return;
  Update_rev(tr[x].s[0]);Update_rev(tr[x].s[1]);tr[x].rev=0;
}

void Rotate(int x){
  int y=tr[x].fa,z=tr[y].fa,k=tr[y].s[1]==x;
  if (!Isroot(y)) tr[z].s[tr[z].s[1]==y]=x;tr[x].fa=z;
  tr[y].s[k]=tr[x].s[k^1];if (tr[x].s[k^1]) tr[tr[x].s[k^1]].fa=y;
  tr[x].s[k^1]=y;tr[y].fa=x;
  Pushup(y);Pushup(x);
}

void Splay(int x){
  int y,z;
  tmp[tt=1]=x;
  for (int i=x;!Isroot(i);i=tr[i].fa) tmp[++tt]=tr[i].fa;
  for (;tt;--tt) Pushdown(tmp[tt]);
  while (!Isroot(x)){
  	y=tr[x].fa;z=tr[y].fa;
  	if (!Isroot(y)) tr[z].s[0]==y^tr[y].s[0]==x?Rotate(x):Rotate(y);
  	Rotate(x);
  }
}

void Access(int x){for (int t=0;x;t=x,x=tr[x].fa) Splay(x),tr[x].s[1]=t,Pushup(x);}
void Makeroot(int x){Access(x);Splay(x);Update_rev(x);}
void Split(int x,int y){Makeroot(x);Access(y);Splay(y);}
int Root(int x){Access(x);Splay(x);while (tr[x].s[0]) x=tr[x].s[0];return x;}
void Link(int x,int y){if (Root(x)==Root(y)) return;Makeroot(x);tr[x].fa=y;}
void Cut(int x,int y){Split(x,y);if (tr[y].s[0]^x||tr[y].s[1]||tr[x].s[1]) return;tr[y].s[0]=tr[x].fa=0;}
int Query_deep(int x){Split(x,n+1);return tr[n+1].siz;}
int Query_second_max(int x,int y){Makeroot(y);Access(x);Splay(y);return tr[tr[y].s[1]].max;}

Abigail into(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;++i) tr[i].siz=1;
  for (int i=1;i<=n;++i){
    scanf("%d",&a[i]);
    Link(i,i+a[i]>n?n+1:i+a[i]);
  }
}

Abigail getans(){
  int opt,u,ans1,ans2;
  for (int i=1;i<=m;++i){
  	scanf("%d",&opt);
  	if (opt==0){
  	  scanf("%d",&u);
  	  Cut(u,u+a[u]>n?n+1:u+a[u]);
  	  scanf("%d",&a[u]);
  	  Link(u,u+a[u]>n?n+1:u+a[u]);
  	}else{
  	  scanf("%d",&u);
  	  printf("%d %d\n",Query_second_max(u,n+1),Query_deep(u)-1);
  	}
  }
}

int main(){
  into();
  getans();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值