您的位置:首页 > 其它

HDU 5967 小R与手机(Link Cut Tree 基环树森林)

2017-10-06 10:15 465 查看

小R与手机

[b]Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 536    Accepted Submission(s): 122
[/b]

Problem Description
小R有n部手机,为了便于管理,他对一些手机设置了“呼叫转移”的功能。

具体来说,第i ( 1 <= i <= n )部手机有个参数 ai(0<=ai<=n,ai≠
i )。若 ai

0 则表示第i部手机接到电话时会将电话无条件转移给第 ai部手机(此时如果
aai≠0,
会继续进行呼叫转移)。

如果一部手机接到电话会导致至少 109次呼叫转移,则这次电话无法接通。

现在有m个事件依次发生,具体如下:
∙1
x y表示将第x部手机的参数设置为y,即将 ax设置为y
;
∙2
x表示询问给第x部手机打电话,最终接到电话的手机编号(如果无法接通, 则编号为-1 )。

小R当然知道怎么做啦!但是他想考考你。
Input

只包含一组测试数据。

输入的第一行有两个整数n, m。保证1 <= n, m <= 2 * 105。

接下来一行,包含n个整数,第i个整数为 ai。

接下来m行,每一行为一个事件,具体格式见问题描述。

对于事件1,保证1 <= x <= n, 0 <= y <= n, x ≠
y ;对于事件2,保证1 <= x <= n
Output

对每个询问事件输出一行一个整数,表示最终接到电话的手机编号,如果无法接 通,输出-1。

Sample Input

5 6
2 3 4 5 0
2 2
2 5
1 4 3
2 1
1 4 0
2 1

Sample Output

5
5
-1
4

Source

2016年中国大学生程序设计竞赛(合肥)-重现赛(感谢安徽大学)

        基环树森林……

        当时还没有意识到是这个东西,直到WA了一发之后。联系起之前做的那道BZOJ 2759,这两题其实很是相似。

        在此我就再总结一下如何处理这种基环树森林。首先,不用说的,用LCT。然后我们连接的时候要提前判断两点是否在同根树中。如果在的话,说明再连上这条边之后就会形成一个环,而实际上为了保持树的结构,我们并不能真的去相连,用一个数组表示虚边。当我们断开或者改变某个点的前驱之后,这个保存的虚边就可以发挥作用了。如果切断的点的实根(非splay根)含有虚边,那么切断的同时可以把这个虚边变成实边。否则如果这个实根就是我切断的点,那么删除之前的虚边。其他情况不做任何改变。

        之前在写BZOJ 2759的时候讨论的比较复杂,但是这次这样子讨论就简单了很多,直接把操作放在了cut里面进行,在BZOJ 2759上改了之后,实测也是可以AC的。所以说,以后就把这个当作基环树的一个模板保存下来,之后直接套用即可。具体见代码(此题没有在树上维护数值):

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

stack<int> sta;
int v
,n,m;

struct Link_Cut_Tree
{
int son
[2],fa
;
bool rev
;

inline bool which(int x){return son[fa[x]][1]==x;}
bool isroot(int x){return !fa[x]||son[fa[x]][which(x)]!=x;}

inline void Reverse(int x)
{
if (!x) return;
swap(son[x][0],son[x][1]);
rev[x]^=1;
}

inline void push_down(int x)
{
if (!x||!rev[x]) return;
Reverse(son[x][0]);
Reverse(son[x][1]);
rev[x]=0;
}

inline void Rotate(int x)
{
int y=fa[x]; bool ch=which(x);
son[y][ch]=son[x][ch^1];son[x][ch^1]=y;
if (!isroot(y)) son[fa[y]][which(y)]=x;
fa[x]=fa[y]; fa[y]=x; fa[son[y][ch]]=y;
}

inline void splay(int x)
{
int i=x;
for(;!isroot(i);i=fa[i])
sta.push(i); sta.push(i);
while (!sta.empty())
{
push_down(sta.top());
sta.pop();
}
while (!isroot(x))
{
int y=fa[x];
if (!isroot(y))
{
if (which(x)^which(y)) Rotate(x);
else Rotate(y);
}
Rotate(x);
}
}

inline void access(int x)
{
int y=0;
while (x)
{
splay(x); son[x][1]=y;
y=x; x=fa[x];
}
}

void beroot(int x){access(x);splay(x);Reverse(x);}

inline bool judge(int x,int y)
{
while(fa[x]) x=fa[x];
while(fa[y]) y=fa[y];
return x==y;
}

inline int getroot(int x)
{
access(x); splay(x);
while (son[x][0]) x=son[x][0];
return x;
}

inline int link(int x,int y)
{
if (judge(x,y)) return y;
beroot(x); fa[x]=y; return 0;
}

inline void cut(int x)
{
int rt=getroot(x);
son[x][0]=fa[son[x][0]]=0;
if (v[rt]&&!judge(rt,v[rt])) v[rt]=link(rt,v[rt]);
else if (rt==x) v[rt]=0;
}

} LCT;

int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int x; scanf("%d",&x);
if (!x) continue;
v[i]=LCT.link(i,x);
}
for(int i=1;i<=m;i++)
{
int op,x,y;
scanf("%d",&op);
if (op==1)
{
scanf("%d%d",&x,&y);
LCT.cut(x);
if (!y) continue;
v[x]=LCT.link(x,y);
} else
{
scanf("%d",&x);
int ans=LCT.getroot(x);
if (v[ans]) {puts("-1");continue;}
printf("%d\n",ans);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: