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;Gate11 */12 13 #include14 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 /// DP49 int lm = (1 << n) - 1, lm2 = n * (n - 1) / 2; /// lm : 1111111111150 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 }
然后我们必须开始找规律了......这里我是完全没思路(太过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 #include14 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 }