BZOJ4881 [Lydsy2017年5月月賽]線段遊戲

觀察一下題意就是讓你把排列分成兩個沒有逆序的序列,問方案數

那麼把每個逆序對連邊,容易發現如果是二分圖,答案就是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;
}

/*

*/


發佈了404 篇原創文章 · 獲贊 120 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章