[BZOJ4881][Lydsy1705月赛]线段游戏

首先冷静一下看清问题的本质,是将整个数列分成两个递增子序列。

那么由Dilworth定理得,无解当且仅当数列的最长下降子序列的长度>2,先特判掉。

然后就有一些比较厉害的做法:http://www.cnblogs.com/Gloid/p/10025835.html

一种比较直观的做法是,将每对逆序对连边,答案就是连通块的个数。

考虑优化这个暴力,从前往后处理,每个连通块用块内最大的数作为代表,用set维护代表。每次加入一个数时,将set中所有大于这个数的数都删去(这些数代表的连通块合并了),然后将最大的那个放进去(新的大连通块的代表)。

 1 #include<set>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 using namespace std;
 6 
 7 const int N=100010,mod=998244353;
 8 int n,ans,tot,a[N],f[N];
 9 set<int>S;
10 
11 int find(int x){
12     int L=1,R=tot;
13     while (L<R){
14         int mid=(L+R)>>1;
15         if (x<=f[mid]) L=mid+1; else R=mid;
16     }
17     return L;
18 }
19 
20 int main(){
21     freopen("bzoj4881.in","r",stdin);
22     freopen("bzoj4881.out","w",stdout);
23     scanf("%d",&n);
24     rep(i,1,n) scanf("%d",&a[i]);
25     f[1]=a[1]; tot=1;
26     rep(i,2,n){
27         if (a[i]<f[tot]){
28             tot++,f[tot]=a[i];
29             if (tot>2){ puts("0"); return 0; }
30         }else{
31             int t=find(a[i]); f[t]=max(f[t],a[i]);
32         }
33     }
34     rep(i,1,n){
35         int t=a[i];
36         while (!S.empty()){
37             set<int>::iterator it=S.upper_bound(t);
38             if (it==S.end()) break;
39             t=*it; S.erase(t);
40         }
41         S.insert(t);
42     }
43     tot=S.size(); ans=1;
44     rep(i,1,tot) ans=2ll*ans%mod;
45     printf("%d\n",ans);
46     return 0;
47 }

 

posted @ 2018-12-15 12:19  HocRiser  阅读(213)  评论(0编辑  收藏  举报