bzoj4881 线段游戏——上升序列方案数

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4881

连题意都转化不了了...

题意是要求从一个数列中选出两个上升序列的方案数;

先判断是否有解,如果最长下降子序列长度>2(有两条以上的线相互交织)则无解,先用树状数组判断一下;

分成一个个“连通块”来考虑,相互交织的几条线为一个连通块,连通块之间互不影响;

在 set 中留一个最大的元素作为此连通块的代表,也判断下一个元素是否在同一个连通块;

每个连通块有两种选法,所以最后答案就是 2^( set 剩余元素个数)。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
using namespace std;
int const maxn=1e5+5,mod=998244353;
int n,p[maxn],f[maxn],ans;
set<int>s;
int rd()
{
    int ret=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
    return ret;
}
void add(int x,int val)
{
    for(;x<=n;x+=(x&-x))f[x]=max(f[x],val);
}
int query(int x)
{
    int ret=0;
    for(;x;x-=(x&-x))ret=max(ret,f[x]);
    return ret;
}
int main()
{
    n=rd();
    for(int i=1;i<=n;i++)p[i]=rd();
    for(int i=n;i;i--)
    {
        int x=query(p[i])+1;
        if(x>2)
        {
            printf("0");return 0;
        }
        add(p[i],x);
    }
    set<int>::iterator it;
    for(int i=1;i<=n;i++)
    {
        int x=p[i];
        while(!s.empty())
        {
            it=s.upper_bound(x);
            if(it==s.end())break;
            x=*it; s.erase(x);
        }
        s.insert(x);
    }
    int m=s.size(); ans=1;
    while(m--)(ans*=2)%=mod;
    printf("%d",ans);
    return 0;
}

 

posted @ 2018-06-15 17:38  Zinn  阅读(203)  评论(0编辑  收藏  举报