1. 程式人生 > 其它 >[cf1097E]Egor and an RPG game

[cf1097E]Egor and an RPG game

構造形如$1,3,2,6,5,4,10,9,8,7,...$的序列,不難發現其中前$\frac{k(k+1)}{2}$項最少要劃分為$k$個單調子序列

由此,取$k=f(n)+1$時應有$\frac{k(k+1)}{2}>n$,也即有$f(n)\ge \max_{\frac{k(k+1)}{2}\le n}k$

令$g(n)$為後者,那麼只需要保證劃分為不超過$g(n)$個單調子序列即可

考慮其最長上升子序列,假設長度為$x$,對其分類討論:

1.若$x\ge g(n)+1$,則有$g(n-x)+1\le g(n)$,那麼直接選擇該序列即可

關於這個式子,即求證$\frac{g(n)(g(n)+1)}{2}>n-(g(n)+1)$,化簡後也即$\frac{(g(n)+1)(g(n)+2)}{2}>n$,那麼如果其不滿足即與$g(n)$的最大性矛盾

2.若$x\le g(n)$,注意到原序列可以被劃分為$x$個下降子序列,直接劃分為下降子序列即可

直接暴力即可,注意到$g(n)$是$o(\sqrt{n})$級別的,複雜度為$o(n\sqrt{n}\log n)$

對於這個複雜度,如果$\log$是線段樹複雜度可能無法通過,需要使用經典的dp+二分求LIS,下面來分別考慮兩種序列如何劃分——

對於最長上升子序列,記錄每一個數字插入時上一個位置的數字,往前找即可

對於下降子序列,注意到每一個位置上的數都單調遞增,即分別構成一個下降子序列

總複雜度為$o(n\sqrt{n}\log n)$,且常數較小,可以通過

 1 #include<bits/stdc++.h>
 2
using namespace std; 3 #define N 100005 4 #define ll long long 5 vector<int>ans[N]; 6 int t,n,m,tot,a[N],val[N],Pos[N],pre[N],fi[N],nex[N],vis[N]; 7 int main(){ 8 scanf("%d",&t); 9 while (t--){ 10 scanf("%d",&n); 11 for(int i=1;i<=n;i++)scanf("%d",&a[i]);
12 m=0; 13 while (n){ 14 tot=0; 15 for(int i=1;i<=n;i++){ 16 int pos; 17 if ((!tot)||(val[tot]<a[i])){ 18 pos=++tot; 19 fi[pos]=i; 20 } 21 else{ 22 pos=lower_bound(val+1,val+tot+1,a[i])-val; 23 nex[Pos[pos]]=i; 24 } 25 val[pos]=a[i],Pos[pos]=i; 26 if (pos==1)pre[i]=0; 27 else pre[i]=Pos[pos-1]; 28 } 29 if ((ll)tot*(tot+1)/2<=n){ 30 for(int i=1;i<=tot;i++){ 31 ans[++m].clear(); 32 for(int j=fi[i];j!=Pos[i];j=nex[j])ans[m].push_back(a[j]); 33 ans[m].push_back(a[Pos[i]]); 34 } 35 break; 36 } 37 ans[++m].clear(); 38 for(int i=1;i<=n;i++)vis[i]=0; 39 for(int i=Pos[tot];i;i=pre[i])vis[i]=1; 40 int nn=0; 41 for(int i=1;i<=n;i++){ 42 if (!vis[i])a[++nn]=a[i]; 43 else ans[m].push_back(a[i]); 44 } 45 n=nn; 46 } 47 printf("%d\n",m); 48 for(int i=1;i<=m;i++){ 49 printf("%d",ans[i].size()); 50 for(int j=0;j<ans[i].size();j++)printf(" %d",ans[i][j]); 51 printf("\n"); 52 } 53 } 54 return 0; 55 }
View Code