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; }