COCI2014 stogovi

题目描述:

Mirko在玩堆栈游戏。开始他有一个空的堆栈,编号为0.在第i步(1<=i<=300000),他会选择一个编号为v的堆栈,做如下操作:
1.a v 表示将v号堆栈复制一份,新栈的编号为i,并将元素i压入新栈的栈顶。
2. b v 表示将v号堆栈复制一份,新栈的编号为i,将新栈的栈顶元素弹出。
3.c v w 将v号堆栈复制一份,编号为i,并比较第v号和第w号堆栈中有多少相同的数。

输入:

第一行一个整数n,表示有n步。
接下来n步,每步表示一个操作,如上所述。

输出:

对所有的b操作和c操作,输出结果,每行一个。b操作需要输出该栈移除的元素,c操作表示两个堆栈的相同的数的个数。

样例输入:

5
a 0
a 1
b 2
c 2 3
b 4

样例输出:

2
1
2

分析:

这是一道比较神的题。用堆栈打掩护,其实是一道LCA的题。在考试的时候虽然隐约感觉这些操作与之前的堆栈有所联系,但没有明确的想出来怎样建图(以前没怎么独立做过LCA的题),乱码了200行,一分没有……下来自己又码了一边,居然一边就A了,可能跟我抄了接近十遍的NOIP2013 day2 truck 有很大的关系吧。“a”操作我们将它翻译为在相关节点连一条父子边,i为输入的x的儿子;“b”操作翻译为将i与输入的x的父亲合并在一起,并输出x的栈顶元素;“c”操作翻译为将i与输入的x1合并,寻找i与输入的x2的最近公共祖先,并输出它的深度。因为题目中给出信息每一次压入堆栈的元素并不相同,所以不用担心在lca(i,x2)后面会有相同的数,就可以直接输出deep[lca(i,x2)]。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 300000
#define MAXD 20
using namespace std;
int fa[MAXN+5][MAXD+5],deep[MAXN+5];
int unit[MAXN+5],top[MAXN+5];
int n,d1,d2;
char o[2];
int find(int x){return unit[x]==x?x:unit[x]=find(unit[x]);}
void dfs(int u,int f)
{
	int i;
	fa[u][0]=f;
	deep[u]=deep[f]+1;
	for(i=1;i<=MAXD;i++)
	{
		if(deep[u]<(1<<i))
			break;
		fa[u][i]=fa[fa[u][i-1]][i-1];
	}
}
int lca(int u,int v)
{
	if(deep[u]<deep[v])
		swap(u,v);
	int i,t=deep[u]-deep[v];
	for(i=0;i<=MAXD;i++)
		if((1<<i)&t)
			u=fa[u][i];
	for(i=MAXD;i>=0;i--)
		if(fa[u][i]!=fa[v][i])
		{
			u=fa[u][i];
			v=fa[v][i];
		}
	if(u==v)
		return u;
	return fa[u][0];
}
int main()
{
	int i;
	scanf("%d",&n);
	memset(fa,-1,sizeof fa);
	for(i=1;i<=n;i++)
		unit[i]=i;
	for(i=1;i<=n;i++)
	{
		scanf("%s",o);
		if(o[0]=='a')
		{
			scanf("%d",&d1);
			d1=find(d1);
			dfs(i,d1);
			top[i]=i;
		}
		else if(o[0]=='b')
		{
			scanf("%d",&d1);
			d1=find(d1);
			int f=find(fa[d1][0]);
			top[i]=top[f];
			dfs(i,f);
			unit[i]=f;
			printf("%d\n",top[d1]);
		}
		else if(o[0]=='c')
		{
			scanf("%d%d",&d1,&d2);
			d1=find(d1),d2=find(d2);
			top[i]=top[d1];
			int f1=find(fa[d1][0]),f2=find(d2);
			dfs(i,f1);
			unit[i]=d1;
			int fi=find(i);
			printf("%d\n",deep[lca(fi,f2)]);
		}
	}
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值