给 $m$ 个 $n$ 元排列 $p_1, p_2, ..., p_n$ ,将它们按照字典序从小到大排列,构成 $q_1, q_2, ..., q_n$ ,将 $q_1, q_2, ..., q_n$ 连接得到 $S$ ,求
$$\sum_{i \in [1, m \times n]} iS_i \mod 2 ^ {32}$$
为了防止大量的读入,我们用一个随机种子 $x$ 来表示一个排列 $p$ ,具体的算法用下面的伪代码描述:
Proc Rand_permutation(p, x): for i in range [1, n]: p[i] = i for i in range [2, n]: swap(p[i], p[x % (i-1) + 1]) x = (x * x + 19260817) % (1 << 32)
$m \le 10 ^ 6, n \le 20, x \in [0, 2 ^ {32})$
64 MB 空间限制。
康托展开。
用树状数组应该可以优化进一秒。
#include <bits/stdc++.h> using namespace std; #define F(i, s, t) for (int i = (s), _ = (t); i <= _; i ++) #define ui unsigned int #define LL long long const int M = 1000005; const int N = 25; int m, n; LL fac[N]; LL a[M]; int p[N]; namespace Input { const int S = 10000000; char s[S], *h = s+S, *t = h; char nchar() { if (h == t) fread(s, 1, S, stdin), h = s; return *h ++; } LL rd() { LL x = 0; char c; do { c = nchar(); } while (!isdigit(c)); do { x = x*10+c-‘0‘, c = nchar(); } while (isdigit(c)); return x; } } using Input::rd; void In(int *p, ui x) { F(i, 1, n) p[i] = i; F(i, 2, n) { swap(p[i], p[x % (i-1) + 1]); x = x * x + 19260817; } } LL Enc(int *p) { LL w = 0; F(i, 1, n) { int c = 0; F(j, i, n) c += (p[i] > p[j]); w += c * fac[n-i]; } return w + 1; } void Dec(int *p, LL w) { w --; static int vi[N]; memset(vi, 0, sizeof vi); F(i, 1, n) { int sma = w / fac[n-i], j; w %= fac[n-i]; for (j = 0; sma >= 0; sma -= ! vi[++ j]); p[i] = j, vi[j] = 1; } } int main() { freopen("con.in", "r", stdin); m = rd(), n = rd(); fac[0] = 1; F(i, 1, n) fac[i] = fac[i-1] * i; F(i, 1, m) { ui r = rd(); In(p, r); a[i] = Enc(p); } sort(a+1, a+m+1); int tot = 0; ui ans = 0; F(i, 1, m) { Dec(p, a[i]); F(j, 1, n) ans += (ui)(++ tot) * p[j]; } cout << ans << endl; return 0; }