T1T3过难就只能放弃了。
题目描述
一共有
2
n
2n
2n 个节点按顺序排布在一行上面,他们分成了
n
n
n 对,每一对之间有一根弦,小F需要把一段节点取出来检查。
他一共会取出
m
m
m 次,每次会选定两个右端点
r
1
r_1
r1;
r
2
r_2
r2,取出来一段节点的同时不能割断任何一根弦,但是无聊的小C想确认符合条件的左端点的个数,也就说这个左端点和两个右端点(含端点)形成的区间取出来以后都不会割断弦。
输入格式
两个正整数
n
,
m
n,m
n,m,表示弦个数和检查次数
2
n
2n
2n 个整数:第
i
i
i 个整数表示第
i
i
i 个点连接这根弦的另一端的点
m
m
m 行,每行两个整数
r
1
,
r
2
r_1,r_2
r1,r2 表示两个右端点,
r
r
r 有可能是
0
0
0 。
输出格式
m m m 行,每行一个整数表示答案。
数据范围
对于所有测试点, n , m ≤ 100 W n,m \le 100W n,m≤100W。
解析
考虑一个右端点,定义它的父亲为最近的左端点使得它俩可以构成一个合法区间的左侧的那一个点,那么这个合法区间一定是极小合法区间,而两个合法区间的交一定合法,一个合法区间要么是交集,要么是极小合法区间,因此可以形成一个森林结构,任意一个祖先(+1)到后代的路径都对应一个合法区间,那么题目中要求的实际上就是 LCA 的 depth,这就比较容易了,只是注意特殊情况。
重点:
- 找极小合法区间:利用 栈 做括号匹配的问题。
- 根节点的确立:当一个点入度==0,就是根。
我为了适应平均的std码风,改用动态数组来建单向边。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int n,m,p[2222222];
int l[2222222],r[2222222],sta[2222222],topp;
int tot,rt[2222222],fa[2222222];
vector<int>edge[2222222];
int f[2222222][22],dep[2222222];
inline int Read(){
register int x=0;register char ch=getchar();
while(ch>57||ch<48)ch=getchar();
while(ch<=57&&ch>=48)x=(x<<3)+(x<<1)+ch-48,ch=getchar();
return x;
}
inline void Write(int x){
if(x>=10)Write(x/10);
putchar(x%10+48);
}
void Init(int u,int rot){
rt[u]=rot;dep[u]=fa[u]==-1?1:dep[fa[u]]+1;
f[u][0]=fa[u];
for(register int i=0;i<=21;i++)f[u][i+1]=f[f[u][i]][i];
int w=edge[u].size();
for(register int i=0;i<=w-1;i++){
int v=edge[u][i];
Init(v,rot);
}
}
int Lca(int u,int v){
if(dep[u]<dep[v])swap(u,v);
for(register int k=dep[u]-dep[v],i=0;(1<<i)<=k;i++)if(k>>i&1)u=f[u][i];
if(u==v)return u;
for(register int i=21;i>=0;i--)if((1<<i)<dep[u]&&f[u][i]!=f[v][i])u=f[u][i],v=f[v][i];
return fa[u];
}
int Query(int r1,int r2){
return rt[r1]==-1||rt[r2]==-1||rt[r1]!=rt[r2]?0:dep[Lca(fa[r1],fa[r2])];
}
int main(){
n=Read();m=Read();
for(register int i=1;i<=n*2;i++)p[i]=Read();
for(register int i=1;i<=n*2;i++){
l[i]=min(i,p[i]),r[i]=max(i,p[i]);
while(topp&&sta[topp]>=l[i]){
int pos=sta[topp--];
l[i]=min(l[i],l[pos]),r[i]=max(r[i],r[pos]);
}
sta[++topp]=i;
}
for(register int i=0;i<=(n<<1);i++)rt[i]=fa[i]=-1;
for(register int i=1;i<=(n<<1);i++){
if(i==r[i]){
fa[i]=l[i]-1;
edge[fa[i]].push_back(i);
}
}
r[0]=0;
for(register int i=0;i<=n*2;i++)if(!edge[i].empty()&&rt[i]==-1)Init(i,i);
while(m--){
int r1=Read(),r2=Read();
if(r1==0||r2==0)putchar(48);
else Write(Query(r1,r2));
putchar(10);
}
return 0;
}
在U盘上运行大约20秒,在评测机上大约1.2秒,时限2秒,内存约333兆。