CF1097E Egor and an RPG game

最少反链划分数 = 最长链。实现:每次找出所有极大元作为一个反链。

任意长度小于k * (k + 1) / 2的排列都能被划分为不多于k个单调序列。且这是一个紧的上界。

然后这题就可以切了。

题意:给定长为n的排列,设任长为n的排列都能被划分为不多于k个单调序列,把给定排列划分为不多于k个单调序列。

解:先求出k,然后求LIS,如果LIS >= k那么扔掉LIS递归。否则划分成反链即可。用链表实现。

 

  1 #include <bits/stdc++.h>
  2 
  3 const int N = 100010, INF = 0x3f3f3f3f;
  4 
  5 int p[N], cnt;
  6 std::vector<int> v[N];
  7 
  8 struct Node {
  9     int nex, pre;
 10 }node[N]; int tp, head = N - 1, tail = N - 2;
 11 
 12 int stk[N], top, f[N], rest, stk2[N], fr[N];
 13 bool vis[N];
 14 
 15 inline void del(int x) {
 16     int nex = node[x].nex, pre = node[x].pre;
 17     node[nex].pre = pre;
 18     node[pre].nex = nex;
 19     return;
 20 }
 21 
 22 inline int getLIS() {
 23     top = 0;
 24     for(int i = node[head].nex; i != tail; i = node[i].nex) {
 25         int l = 0, r = top;
 26         while(l < r) {
 27             int mid = (l + r + 1) >> 1;
 28             if(stk[mid] < p[i]) {
 29                 l = mid;
 30             }
 31             else {
 32                 r = mid - 1;
 33             }
 34         }
 35         f[i] = r + 1;
 36         fr[i] = stk2[r];
 37         stk[r + 1] = p[i];
 38         stk2[r + 1] = i;
 39         if(r == top) ++top;
 40     }
 41     return top;
 42 }
 43 
 44 inline void erase() {
 45     int x = stk2[top];
 46     ++cnt;
 47     while(x) {
 48         del(x);
 49         v[cnt].push_back(p[x]);
 50         x = fr[x];
 51     }
 52     std::reverse(v[cnt].begin(), v[cnt].end());
 53     return;
 54 }
 55 
 56 inline void split() {
 57     
 58     while(rest) {
 59         ++cnt;
 60         int last = -1;
 61         for(int i = node[tail].pre; i != head; i = node[i].pre) {
 62             if(p[i] > last) {
 63                 v[cnt].push_back(i);
 64                 last = p[i];
 65                 //printf("inside %d \n", i);
 66                 rest--;
 67             }
 68         }
 69         std::reverse(v[cnt].begin(), v[cnt].end());
 70         int len = v[cnt].size();
 71         for(int i = 0; i < len; i++) {
 72             del(v[cnt][i]);
 73             //printf("now : %d \n", v[cnt][i]);
 74             v[cnt][i] = p[v[cnt][i]];
 75             //printf("now : %d \n", v[cnt][i]);
 76         }
 77     }
 78     
 79     return;
 80 }
 81 
 82 inline void solve() {
 83     int n;
 84     scanf("%d", &n);
 85     for(int i = 1; i <= n; i++) {
 86         scanf("%d", &p[i]);
 87         node[i].nex = 0;
 88         if(i > 1) {
 89             node[i].pre = i - 1;
 90             node[i - 1].nex = i;
 91         }
 92         else {
 93             node[i].pre = head;
 94             node[head].nex = i;
 95         }
 96     }
 97     node[n].nex = tail;
 98     node[tail].pre = n;
 99     cnt = 0;
100     rest = n;
101     int K = 1;
102     while(K * (K + 1) / 2 <= n) {
103         ++K;
104     }
105     //printf("K = %d \n", K);
106     /// use K-1 
107     while(rest) {
108         int len = getLIS();
109         //printf("len = %d \n", len);
110         if(len >= K) {
111             erase();
112             K--;
113             rest -= len;
114         }
115         else {
116             split();
117             rest = 0;
118         }
119     }
120     printf("%d \n", cnt);
121     for(int i = 1; i <= cnt; i++) {
122         int len = v[i].size();
123         printf("%d ", len);
124         for(int j = 0; j < len; j++) {
125             printf("%d ", v[i][j]);
126         }
127         puts("");
128         v[i].clear();
129     }
130     tp = 0;
131     return;
132 }
133 
134 int main() {
135     
136     int T;
137     scanf("%d", &T);
138     while(T--) {
139         solve();
140     }
141     
142     return 0;
143 }
AC代码

 

posted @ 2019-06-22 21:41  huyufeifei  阅读(260)  评论(0编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜