您的位置:首页 > 其它

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

2017-05-15 21:12 363 查看
观察一下题意就是让你把排列分成两个没有逆序的序列,问方案数

那么把每个逆序对连边,容易发现如果是二分图,答案就是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&-x
set<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;
}

/*

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: