191106CSP模拟DAY2

今天这个题做的让我怀疑人生,所以只写了T2的题解。

T2:我的订书机之恋(巧妙建图,LCA)

考场上我连题都没读懂,qwq。

其实就是给定 n n n个区间,每次给你 2 2 2个右端点,让你求出所有合法的左端点的个数。

我们可以每一个点作为右端点时产生的极小答案区间,很显然,这些合法的极小答案区间并起来也是合法的,我们从每个右端点向左跳,若跳到左端点,则不会合法,因为继续跳,会超过区间;而跳到右端点就继续跳,直到不合法。

而另一个难点,就是我们巧妙地从 r + 1 r+1 r+1 l l l连边,倍增求出 l c a lca lca r 1 和 r 2 r1和r2 r1r2 l c a lca lca的深度即为个数。

代码:

#include<bits/stdc++.h>
#define ll long long
#define db double
#define re register
#define cs const
#define N 3000005
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch;
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
stack<int>q;
int tot,n,m,r1,r2;
int p[N],f[N][22],dep[N],first[N],to[N*2],net[N*2];
int l[N],r[N],bj[N];
bool vis[N];
void add(int x,int y)
{
	net[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}
void dfs(int x,int fa,int y)
{
	bj[x]=y;
	dep[x]=dep[fa]+1;
	f[x][0]=fa;
	for(re int i=1;i<=20;++i)	f[x][i]=f[f[x][i-1]][i-1];
	for(re int e=first[x];e;e=net[e])
	{
		int v=to[e];
		if(v==fa)	continue;
		dfs(v,x,y);
	}
}
int Lca(int x,int y)
{
	if(dep[x]<dep[y])	swap(x,y);
	for(re int i=20;i>=0;i--)
	{
		if(dep[f[x][i]]>=dep[y])	x=f[x][i];
	}
	if(x==y)	return x;
	for(re int i=20;i>=0;--i)
	{
		if(f[x][i]!=f[y][i])	x=f[x][i],y=f[y][i];
	}
	return f[x][0];
}
int main()
{
	n=read();
	m=read();
	for(re int i=1;i<=2*n;++i)	p[i]=read();
	for(re int i=1;i<=2*n;++i)
	{
		l[i]=min(p[i],i);
		r[i]=max(p[i],i);
		while(!q.empty()&&q.top()>=l[i])
		{
			int x=q.top();
			l[i]=min(l[i],l[x]);
			r[i]=max(r[i],r[x]);
			q.pop();
		}
		q.push(i);
	}
	for(re int i=1;i<=2*n;++i)
	{
		if(i==r[i])
		{
			add(l[i],i+1);
			add(i+1,l[i]);
			vis[l[i]]=vis[i+1]=1;
		}
	}
	for(re int i=1;i<=2*n;++i)	if(vis[i]&&!bj[i])	dfs(i,0,i);
	for(re int i=1;i<=m;++i)
	{
		r1=read();
		r2=read();
		if(!vis[r1+1]||!vis[r2+1]||bj[r1+1]!=bj[r2+1])
		{
			puts("0");
			continue;
		}
		int lca=Lca(r1+1,r2+1);
		int ans=dep[lca];
		if(lca==r1+1||lca==r2+1)	--ans;
		printf("%d\n",ans);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值