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

来源:互联网 发布:linux自动挂载硬盘 编辑:程序博客网 时间:2024/06/01 08:00

观察一下题意就是让你把排列分成两个没有逆序的序列,问方案数

那么把每个逆序对连边,容易发现如果是二分图,答案就是2^(联通块数量),否则无解

那么先把无解判掉,然后从前往后一个数一个数加入,因为现在肯定有解了,所以对于同一个i和a[i],任意两个满足j<i,a[j]>a[i]的j和j',j和j'当前一定属于两个不同的联通块,而现在他们都要和i连边,那么把i加进来之后他们就都变成一个联通块了

而对于一个联通块,我们只需要保存其中最大的元素就可以,因为第二大的元素永远也不会再向后连边了,如果第二大的还能向后连边就说明无解

那么从前往后扫一遍,每次加一个数之后把大于等于这个数的都删掉,再把删掉的数里最大的加回来

最后剩的数的个数就是联通块的数量

#include<iostream>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>#include<iomanip>#include<cstdlib>#include<cstdio>#include<map>#include<bitset>#include<set>#include<stack>#include<vector>#include<queue>using namespace std;#define MAXN 100010#define MAXM 1010#define ll long long#define eps 1e-8#define MOD 998244353#define INF 1000000000#define lb(x) x&-xset<int>s;int c[MAXN];int ans=1;int n;int ask(int x){int re=0;for(;x;x-=lb(x)){re=max(re,c[x]);}return re;}void change(int x,int y){for(;x<=n;x+=lb(x)){c[x]=max(c[x],y);}}int main(){int i,x;scanf("%d",&n);for(i=1;i<=n;i++){scanf("%d",&x);int t=ask(n-x);if(t==2){printf("0\n");return 0;}change(n-x+1,t+1);int mx=x;set<int>::iterator p=s.upper_bound(x);while(p!=s.end()){mx=max(mx,*p);set<int>::iterator tmp=p;p++;s.erase(tmp);}s.insert(mx);}x=s.size();while(x){(ans<<=1)%=MOD;x--;}printf("%d\n",ans);return 0;}/**/