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

 

转载于:https://www.cnblogs.com/HocRiser/p/10122958.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值