U21422排列

Posted sdchr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了U21422排列相关的知识,希望对你有一定的参考价值。

给 $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;
}

 

以上是关于U21422排列的主要内容,如果未能解决你的问题,请参考以下文章

itertools 排列组合

从搜索文档中查找最小片段的算法?

翻转数组

翻转数组

微信小程序代码片段

VSCode自定义代码片段——CSS选择器