BZOJ4881: [Lydsy2017年5月月赛]线段游戏

来源:互联网 发布:叶陌折 知乎 编辑:程序博客网 时间:2024/06/06 18:10

题目链接: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

Sample Output

8

若线段 i j 相交,那么在它们之间连一条边。若这个图不是二分图,那么无解,否则令cnt 为连通块个数,那么 ans = 2cnt

 

在二分图染色的过程中,每个点只需要被访问一次。对于当前所在的点 x,它可以一步走到 [1; xp[i> p[x的所有i,以及 (x; np[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;}


阅读全文
1 0