[bzoj4881][Lydsy2017年5月月赛]线段游戏

来自FallDream的博客,未经允许,请勿转载,谢谢。


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可以赢得游戏。
n<=100000
 
这道题挺有意思的233
如果把线段只要有相交的就成一个块,可以得到很多块。答案应该是2^块的个数次方。
特判无解,也就是如果有三条或者更多的线段互相交织,那么显然无解。只需要对每个块做一次,维护当前右端点最大值,和当前最右的那条线段相交的右端点的最大值即可判定。
网上好像有一些看起来比较靠谱的做法 但是我这个扫一遍就行了,复杂度O(n)  
轻松rank1
#include<iostream>
#include<cstdio>
#define getchar() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:(*S++))
char B[1<<15],*S=B,*T=B;
#define mod 998244353
using namespace std;
inline int read()
{
    int x = 0; char ch = getchar();
    while(ch < '0' || ch > '9')  ch = getchar();
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x;
}

int ans=0,n,a[100005],pre=0;
bool flag=0;

inline int pow(int x,int k)
{
    int sum=1;
    for(;k;k>>=1,x=1LL*x*x%mod)
        if(k&1) sum=1LL*sum*x%mod;
    return sum;
}

inline void Solve(int l,int r)
{
    int mx=0,Mx=0;
    for(register int i=l;i<=r&&!flag;++i)
    {
        if(a[i]<mx&&a[i]<Mx) flag=1;
        if(a[i]>mx) mx=a[i],Mx=0;
        else if(a[i]>Mx) Mx=a[i];
    }
}

int main()
{
    n=read();
    for(register int i=1,Mx=0;i<=n&&!flag;Mx=max(Mx,a[i++])) 
        if((a[i]=read())==i&&Mx<i) Solve(pre+1,i-1),ans+=1+(pre!=i-1),pre=i;
    Solve(pre+1,n);if(pre!=n) ++ans;
    printf("%d\n",flag?0:pow(2,ans));
    return 0;
}
posted @ 2017-05-23 14:04  FallDream  阅读(410)  评论(0编辑  收藏  举报