bzoj 4970: [ioi2004]empodia 障碍段
Description
古数学及哲学家毕氏相信自然之本质为数学。现代生物学家研究生物数列(biosequences)。 生物数数为满足下列
条件之 M 个整数所成的数数:
1: 包含从 0, 1, …, 到 M - 1 的所有数字
2: 起始数字为 0, 最后一个数字为 M - 1
?2:数列中 E+1 不可以紧接在 E 之后
生物数数的连续子数列称为数段(segments)。如果一个数段的起点为该数段最小的数字, 终点为该数段最大的数
字且与起点不是同一个数字,且介于这两个数字之间所有的整数都出现在这个数段中, 则称这个数段为框段(frame
d interval),如果框段中并不包含题名小的框段,则称之为障碍段(empodio)。以(0,3,5,4,6,2,1,7)这个生物数
列为例。 整个生物数?是一个框段, 可是它包含了另外一框段 (3,5,4,6) ,因此该生物数列是障碍段。而框段 (
3,5,4,6) 并不包含任何更短的框段所以它是一个障碍段,而且是此生物数列中唯一的障碍段。请写一个程序, 在
输入生物数列后, 输出所有的障碍段 (empodia 为 empodio的复数形)。
solution
这题比较巧,我们首先意识到,点对一定满足 \(a[i]-a[j]=i-j\),即 \(a[i]-i=a[j]-j\),然后我们枚举等式的值,抠出满足等式的条件的点,然后拿这些点做
然后是一个套路,求出 \(f[i]\) 表示这个点做为最大值能够往左边延伸到的最远位置, \(g[i]\) 表示这个点做为最小值能够往右边延伸到的最远距离,然后满足二维偏序即可
对于点对 \((i,j)\),\(f[j]<=i\),\(g[i]>=j\) 即可满足条件,因为要满足不存在区间包含关系,所以可以维护 \(g[i]\) 的单调栈即可,我们要取的 \(i\) 要尽量小
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <ctime>
#include <cstdlib>
#define il inline
#define RG register
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=1100005;
il int gi(){
RG int str=0;RG char ch=getchar();
while(ch>'9' || ch<'0')ch=getchar();
while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar();
return str;
}
int st[N],top=0,f[N],g[N],n,a[N],head[N*2],nxt[N],to[N],num=0;
int m=0,b[N],bel[N];
void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
void priwork(){
for(int i=1;i<=n;i++){
while(top && a[i]>a[st[top]])top--;
if(top)f[i]=st[top]+1;
st[++top]=i;
}
top=0;
for(int i=n;i>=1;i--){
while(top && a[i]<a[st[top]])top--;
if(top)g[i]=st[top]-1;
st[++top]=i;
}
for(int i=1;i<=n;i++)if(g[i]==0)g[i]=n+1;
for(int i=n;i>=1;i--)link(a[i]-i+n,i);
}
int cnt=0;
void solve(){
top=0;
for(int i=1;i<=m;i++){
while(top && g[st[top]]<b[i])top--;
if(top && st[top]>=f[b[i]])bel[b[i]]=st[top],cnt++;
st[++top]=b[i];
}
}
struct node{
int l,r;
bool operator <(const node &pr)const{return l<pr.l;}
}e[N];
void work()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)a[i]=gi(),a[i]++;
priwork();
for(int i=0;i<=n+n;i++){
m=0;
for(int j=head[i];j;j=nxt[j])
b[++m]=to[j];
if(m<2)continue;
solve();
}
int tot=0;
for(int i=1;i<=n;i++)
if(bel[i])e[++tot].l=bel[i],e[tot].r=i;
sort(e+1,e+tot+1);
top=0;
for(int i=1;i<=tot;i++)
if(e[i].r<=e[top].r)e[top]=e[i];
else e[++top]=e[i];
printf("%d\n",top);
for(int i=1;i<=top;i++)
printf("%d %d\n",e[i].l,e[i].r);
}
int main()
{
work();
return 0;
}