洛谷P4769 冒泡排序

n <= 60w,∑n <= 200w,1s。

解:首先有个全排列 + 树状数组的暴力。

然后有个没有任何规律的状压...首先我想的是按照大小顺序来放数,可以分为确定绝对位置和相对位置两种,但是都不好处理字典序。

然后换个思路一位一位的考虑放哪个数。用一维来记录|i - pi| - 逆序对数 * 2,还有一维记录是否紧贴下限,一个二进制数记录之前填了哪些数。

当前填到哪一位可以通过二进制中1的个数统计出来。这样就是一个n32n的做法,可以过n <= 14的点,得到24分。

 1 /**
 2  * There is no end though there is a start in space. ---Infinity.
 3  * It has own power, it ruins, and it goes though there is a start also in the star. ---Finite.
 4  * Only the person who was wisdom can read the most foolish one from the history.
 5  * The fish that lives in the sea doesn't know the world in the land.
 6  * It also ruins and goes if they have wisdom.
 7  * It is funnier that man exceeds the speed of light than fish start living in the land.
 8  * It can be said that this is an final ultimatum from the god to the people who can fight.
 9  *
10  * Steins;Gate
11  */
12 
13 #include <bits/stdc++.h>
14 
15 const int N = 600010, MO = 998244353;
16 
17 int p[N], n;
18 
19 inline void Add(int &a, const int &b) {
20     a += b;
21     while(a >= MO) a -= MO;
22     while(a < 0) a += MO;
23     return;
24 }
25 
26 inline void out(int x) {
27     for(int i = 0; i < n; i++) {
28         printf("%d", (x >> i) & 1);
29     }
30     return;
31 }
32 
33 namespace Sta {
34     const int DT = 500, M = 33000;
35     int f[DT * 2][M][2], cnt[M], pw[M];
36     inline void prework() {
37         for(int i = 1; i < M; i++) {
38             cnt[i] = 1 + cnt[i - (i & (-i))];
39         }
40         for(int i = 2; i < M; i++) {
41             pw[i] = pw[i >> 1] + 1;
42         }
43         return;
44     }
45     inline void solve() {
46         memset(f, 0, sizeof(f));
47         f[DT][0][0] = 1;
48         /// DP
49         int lm = (1 << n) - 1, lm2 = n * (n - 1) / 2; /// lm : 11111111111
50         for(int s = 0; s < lm; s++) {
51             for(int i = -lm2; i <= lm2; i++) {
52                 /// f[i + DT][s][0/1]
53                 if(!f[i + DT][s][0] && !f[i + DT][s][1]) continue;
54                 //printf("f %d ", i); out(s); printf(" 0 = %d  1 = %d \n", f[i + DT][s][0], f[i + DT][s][1]);
55                 int t = lm ^ s;
56                 while(t) {
57                     int j = pw[t & (-t)] + 1, temp = i + std::abs(cnt[s] + 1 - j) - cnt[s >> (j - 1)] * 2 + DT, t2 = s | (1 << (j - 1));
58                     /// f[i + DT][s][1] -> f[std::abs(cnt[s] + 1 - j) - cnt[s >> (j - 1)] + DT][s | (1 << (j - 1))][1]
59                     Add(f[temp][t2][1], f[i + DT][s][1]);
60                     //printf("1 > %d ", temp - DT); out(t2); printf(" 1 = %d\n", f[temp][t2][1]);
61                     if(j > p[cnt[s] + 1]) {
62                         Add(f[temp][t2][1], f[i + DT][s][0]);
63                         //printf("0 > %d ", temp - DT); out(t2); printf(" 1 = %d\n", f[temp][t2][1]);
64                     }
65                     else if(j == p[cnt[s] + 1]) {
66                         Add(f[temp][t2][0], f[i + DT][s][0]);
67                         //printf("0 > %d ", temp - DT); out(t2); printf(" 0 = %d\n", f[temp][t2][0]);
68                     }
69                     t ^= 1 << (j - 1);
70                 }
71             }
72         }
73 
74         printf("%d\n", f[DT][lm][1]);
75         return;
76     }
77 }
78 
79 int main() {
80     //printf("%d \n", (sizeof(Sta::f)) / 1048576);
81     int T;
82     scanf("%d", &T);
83     Sta::prework();
84     while(T--) {
85         scanf("%d", &n);
86         for(int i = 1; i <= n; i++) {
87             scanf("%d", &p[i]);
88         }
89         Sta::solve();
90     }
91     return 0;
92 }
24分状压

 然后我们必须开始找规律了......这里我是完全没思路(太过SB)

冷静分析一波发现,一个合法的排列等价于一个不存在长度为三的下降子序列的排列。

因为如果存在长度为3的下降子序列,那么中间那个元素一定有一次移动不是如它所想的。而不存在的话,我们就可以归纳,冒泡排序每一步都会减少一对逆序对,下降子序列一定不会变长。(全是口胡,意会就行了)

然后有一个44分的状压DP出来了,我没写...

80分n2DP,先考虑怎么求答案。枚举哪个位置开始自由,如果在i开始自由的话就要知道后面有多少种填法。所以我们需要一个从当前状态到终态的状态,而不是从初态到当前。

(看题解)发现如果前缀最大值为j,那么当前要么填比j小的最小的,要么填比j大的。因为如果填了比j小的又不是最小的,就会有一个长为3的下降子序列。

然后设fi,j表示还剩i位要填,能填的数中比max(1~i)大的有j个,填满的方案数。

于是有f[i][j] = ∑f[i - 1][k] = f[i][j - 1] + f[i - 1][j]

于是枚举在哪自由,然后把对应的j求出来。每个位置要加上fn-i,0~j-1

注意有两个地方要break,一个是后面没有比max(1~i)大的数了,还有就是当前填了长为3的下降子序列了。

100分:发现f这个东西其实就是格路径方案数......特别注意i < j的时候为0,所以有一条线不能跨过......

组合数学一波,就能O(1)计算f了。然后发现这个东西fn-i,0~j-1不就是fn-i+1,j-1吗?然后就完事了,nlogn。

 

  1 /**
  2  * There is no end though there is a start in space. ---Infinity.
  3  * It has own power, it ruins, and it goes though there is a start also in the star. ---Finite.
  4  * Only the person who was wisdom can read the most foolish one from the history.
  5  * The fish that lives in the sea doesn't know the world in the land.
  6  * It also ruins and goes if they have wisdom.
  7  * It is funnier that man exceeds the speed of light than fish start living in the land.
  8  * It can be said that this is an final ultimatum from the god to the people who can fight.
  9  *
 10  * Steins;Gate
 11  */
 12 
 13 #include <bits/stdc++.h>
 14 
 15 typedef long long LL;
 16 const int N = 600010, MO = 998244353;
 17 
 18 int p[N], n;
 19 int fac[N << 1], inv[N << 1], invn[N << 1];
 20 
 21 inline LL C(int n, int m) {
 22     if(n < 0 || m < 0 || n < m) return 0;
 23     return 1ll * fac[n] * invn[m] % MO * invn[n - m] % MO;
 24 }
 25 
 26 inline void Add(int &a, const int &b) {
 27     a += b;
 28     while(a >= MO) a -= MO;
 29     while(a < 0) a += MO;
 30     return;
 31 }
 32 
 33 inline void out(int x) {
 34     for(int i = 0; i < n; i++) {
 35         printf("%d", (x >> i) & 1);
 36     }
 37     return;
 38 }
 39 
 40 namespace Sta {
 41     const int DT = 500, M = 33000;
 42     int f[DT * 2][M][2], cnt[M], pw[M];
 43     inline void prework() {
 44         for(int i = 1; i < M; i++) {
 45             cnt[i] = 1 + cnt[i - (i & (-i))];
 46         }
 47         for(int i = 2; i < M; i++) {
 48             pw[i] = pw[i >> 1] + 1;
 49         }
 50         return;
 51     }
 52     inline void solve() {
 53         memset(f, 0, sizeof(f));
 54         f[DT][0][0] = 1;
 55         /// DP
 56         int lm = (1 << n) - 1, lm2 = n * (n - 1) / 2; /// lm : 11111111111
 57         for(int s = 0; s < lm; s++) {
 58             for(int i = -lm2; i <= lm2; i++) {
 59                 /// f[i + DT][s][0/1]
 60                 if(!f[i + DT][s][0] && !f[i + DT][s][1]) continue;
 61                 //printf("f %d ", i); out(s); printf(" 0 = %d  1 = %d \n", f[i + DT][s][0], f[i + DT][s][1]);
 62                 int t = lm ^ s;
 63                 while(t) {
 64                     int j = pw[t & (-t)] + 1, temp = i + std::abs(cnt[s] + 1 - j) - cnt[s >> (j - 1)] * 2 + DT, t2 = s | (1 << (j - 1));
 65                     /// f[i + DT][s][1] -> f[std::abs(cnt[s] + 1 - j) - cnt[s >> (j - 1)] + DT][s | (1 << (j - 1))][1]
 66                     Add(f[temp][t2][1], f[i + DT][s][1]);
 67                     //printf("1 > %d ", temp - DT); out(t2); printf(" 1 = %d\n", f[temp][t2][1]);
 68                     if(j > p[cnt[s] + 1]) {
 69                         Add(f[temp][t2][1], f[i + DT][s][0]);
 70                         //printf("0 > %d ", temp - DT); out(t2); printf(" 1 = %d\n", f[temp][t2][1]);
 71                     }
 72                     else if(j == p[cnt[s] + 1]) {
 73                         Add(f[temp][t2][0], f[i + DT][s][0]);
 74                         //printf("0 > %d ", temp - DT); out(t2); printf(" 0 = %d\n", f[temp][t2][0]);
 75                     }
 76                     t ^= 1 << (j - 1);
 77                 }
 78             }
 79         }
 80 
 81         printf("%d\n", f[DT][lm][1]);
 82         return;
 83     }
 84 }
 85 
 86 namespace Stable {
 87 
 88     int ta[N];
 89 
 90     inline void add(int i) {
 91         for(; i <= n; i += i & (-i)) ta[i]++;
 92         return;
 93     }
 94     inline int ask(int i) {
 95         int ans = 0;
 96         for(; i; i -= i & (-i)) {
 97             ans += ta[i];
 98         }
 99         return ans;
100     }
101     inline void clear() {
102         memset(ta + 1, 0, n * sizeof(int));
103         return;
104     }
105 
106     inline bool check() {
107         int ans = 0, cnt = 0;
108         for(int i = n; i >= 1; i--) {
109             cnt += std::abs(i - p[i]);
110             ans += ask(p[i] - 1);
111             add(p[i]);
112         }
113         clear();
114         return cnt == ans * 2;
115     }
116 
117     inline int cal() {
118         int ans = 0;
119         for(int i = 1; i <= n; i++) {
120             if(p[i] < p[i - 1]) ans++;
121         }
122         return ans + 1;
123     }
124 
125     inline void work() {
126         for(n = 1; n <= 10; n++) {
127             for(int i = 1; i <= n; i++) p[i] = i;
128             do {
129                 if(check()) {
130                     for(int i = 1; i <= n; i++) {
131                         printf("%d ", p[i]);
132                     }
133                 }
134                 else {
135                     printf("ERR : ");
136                     for(int i = 1; i <= n; i++) {
137                         printf("%d ", p[i]);
138                     }
139                 }
140                 printf("  cal = %d \n", cal());
141             } while(std::next_permutation(p + 1, p + n + 1));
142             getchar();
143         }
144         return;
145     }
146 }
147 
148 namespace DP {
149     const int M = 1010;
150     int f[M][M], ta[N];
151     inline void add(int i) {
152         for(; i <= n; i += i & (-i)) ta[i]++;
153         return;
154     }
155     inline int ask(int i) {
156         int ans = 0;
157         for(; i; i -= i & (-i)) {
158             ans += ta[i];
159         }
160         return ans;
161     }
162     inline int getSum(int l, int r) {
163         return ask(r) - ask(l - 1);
164     }
165     inline int F(int i, int j) {
166         if(i < j) return 0;
167         return (C(i + j - 1, j) - C(i + j - 1, i + 1) + MO) % MO;
168     }
169     inline void solve() {
170         /*memset(f, 0, sizeof(f));
171         f[0][0] = 1;
172         for(int i = 1; i <= n; i++) {
173             for(int j = 0; j <= i; j++) {
174                 f[i][j] = (f[i - 1][j] + f[i][j - 1]) % MO;
175             }
176         }*/
177 
178         /*for(int j = n; j >= 0; j--) {
179             for(int i = 0; i <= n; i++) {
180                 printf("%3d ", f[i][j]);
181             }
182             puts("");
183         }*/
184 
185         int ans = 0, pre = 0;
186         for(int i = 1; i < n; i++) {
187             /// [1, i - 1] ==   i >
188             pre = std::max(pre, p[i]);
189             int k = n - pre - getSum(pre + 1, n);
190             //printf("i = %d k = %d \n", i, k);
191             if(!k) break;
192             /*for(int j = 0; j < k; j++) {
193                 Add(ans, F(n - i, j));
194                 //printf("add f %d %d \n", n - i, j);
195             }*/
196             Add(ans, F(n - i + 1, k - 1));
197             add(p[i]);
198             if(p[i] < pre && ask(p[i]) != p[i]) break;
199         }
200         printf("%d\n", ans);
201         memset(ta + 1, 0, n * sizeof(int));
202         return;
203     }
204 }
205 
206 int main() {
207     //printf("%d \n", (sizeof(Sta::f)) / 1048576);
208     //Stable::work();
209     fac[0] = inv[0] = invn[0] = 1;
210     fac[1] = inv[1] = invn[1] = 1;
211     for(int i = 2; i < N * 2; i++) {
212         fac[i] = 1ll * fac[i - 1] * i % MO;
213         inv[i] = 1ll * inv[MO % i] * (MO - MO / i) % MO;
214         invn[i] = 1ll * invn[i - 1] * inv[i] % MO;
215     }
216     int T;
217     scanf("%d", &T);
218     //Sta::prework();
219     while(T--) {
220         scanf("%d", &n);
221         for(int i = 1; i <= n; i++) {
222             scanf("%d", &p[i]);
223         }
224         DP::solve();
225     }
226     return 0;
227 }
AC代码

 

posted @ 2019-04-08 20:30  huyufeifei  阅读(368)  评论(0编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

ReadEra 阅读书籍

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