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