题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4881
Description
quailty和tangjz正在玩一个关于线段的游戏。在平面上有n条线段,编号依次为1到n。其中第i条线段的两端点坐
标分别为(0,i)和(1,p_i),其中p_1,p_2,...,p_n构成了1到n的一个排列。quailty先手,他可以选择一些互不相交
的线段,将它们拿走,当然他也可以一条线段也不选。然后tangjz必须拿走所有剩下的线段,若有两条线段相交,
那么他就输了,否则他就赢了。注意若quailty拿走了全部线段,那么tangjz也会胜利。quailty深深喜欢着tangjz
,所以他不希望tangjz输掉游戏,请计算他有多少种选择线段的方式,使得tangjz可以赢得游戏。
Input
第一行包含一个正整数n(1<=n<=100000),表示线段的个数。
第二行包含n个正整数p_1,p_2,...,p_n(1<=p_i<=n),含义如题面所述。
Output
输出一行一个整数,即tangjz胜利的方案数,因为答案很大,请对998244353取模输出。
Sample Input
5
1 2 4 5 3
1 2 4 5 3
Sample Output
8
若线段 i 和j 相交,那么在它们之间连一条边。若这个图不是二分图,那么无解,否则令 cnt 为连通块个数,那么 ans = 2cnt。
在二分图染色的过程中,每个点只需要被访问一次。对于当前所在的点 x,它可以一步走到 [1; x) 里p[i] > p[x] 的所有i,以及 (x; n] 里p[j] < p[x] 的所有j。用线段树维护所有没走过
的点,记录每个区间 p 最小与最大的两个位置。每次贪心取出最大/小的,看看是否满足条件,若满足则删除该点,然后递归染色,否则终止。
时间复杂度 O(n log n)。
//二分图染色,线段树加速
#include<cstdio>
#include<cstdlib>
const int N=100010,M=262150;
int n,i,a[N],ma[M],mi[M],pos[N],tmp,col[N],f[3],ret,ans;
inline int mergema(int x,int y){
if(!x||!y)return x+y;
return a[x]>a[y]?x:y;
}
inline int mergemi(int x,int y){
if(!x||!y)return x+y;
return a[x]<a[y]?x:y;
}
inline void up(int x){
ma[x]=mergema(ma[x<<1],ma[x<<1|1]);
mi[x]=mergemi(mi[x<<1],mi[x<<1|1]);
}
void build(int x,int a,int b){
if(a==b){
ma[x]=mi[x]=a;
pos[a]=x;
return;
}
int mid=(a+b)>>1;
build(x<<1,a,mid),build(x<<1|1,mid+1,b);
up(x);
}
inline void del(int x){
x=pos[x];
ma[x]=mi[x]=0;
for(x>>=1;x;x>>=1)up(x);
}
void askma(int x,int a,int b,int c,int d){
if(c<=a&&b<=d){
tmp=mergema(tmp,ma[x]);
return;
}
int mid=(a+b)>>1;
if(c<=mid)askma(x<<1,a,mid,c,d);
if(d>mid)askma(x<<1|1,mid+1,b,c,d);
}
void askmi(int x,int a,int b,int c,int d){
if(c<=a&&b<=d){
tmp=mergemi(tmp,mi[x]);
return;
}
int mid=(a+b)>>1;
if(c<=mid)askmi(x<<1,a,mid,c,d);
if(d>mid)askmi(x<<1|1,mid+1,b,c,d);
}
void dfs(int x,int y){
del(x);
col[x]=y;
y=3-y;
while(1){
tmp=0;
askmi(1,1,n,x,n);
if(tmp&&a[tmp]<a[x])dfs(tmp,y);else break;
}
while(1){
tmp=0;
askma(1,1,n,1,x);
if(tmp&&a[tmp]>a[x])dfs(tmp,y);else break;
}
}
int main(){
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
build(1,1,n);
for(i=1;i<=n;i++)if(!col[i])dfs(i,1),ret++;
for(i=1;i<=n;i++){
if(a[i]<f[col[i]])return puts("0"),0;
f[col[i]]=a[i];
}
ans=1;
while(ret--)ans=ans*2%998244353;
printf("%d",ans);
return 0;
}