今天这个题做的让我怀疑人生,所以只写了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 r1和r2的 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);
}
}