1 条题解

  • 0
    @ 2025-8-24 23:04:33

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar EuphoricStar
    Remember.

    搬运于2025-08-24 23:04:33,当前版本为作者最后更新于2024-09-29 15:36:59,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    首先考虑怎么对一个极好的排列 ppf(p)f(p)

    枚举一个正整数 xx,把排列中 x\le x 的数设成 00>x> x 的数设成 11

    那么交换这个排列使得 maxi=1xpi=x\max\limits_{i = 1}^x p_i = x 的最小操作次数就是这个 0101 串的逆序对数。

    交换这个排列使其变成好的的最小操作次数就是对于所有可能的 xx 对应的 0101 串的逆序对数的最小值。

    因为 pp 是极好的,所以最小值对应的那个 0101 串一定是唯一的。称这个 0101 串是排列 pp关键串

    把这个 0101 串拿出来,那么 f(p)f(p) 就是这个 0101 串,一次可以交换相邻的一对 1010,交换使得其有序的交换方案个数。

    考虑把 00 看成向右走,11 看成向上走,这样可以画出来一个杨表的轮廓。然后交换相邻的一对 1010 等价于删掉杨表中处于其所在行第一个和其所在列第一个的格子。可以发现方案数就是这个杨表的勾长公式。

    因为 n80n \le 80 所以杨表格子数 79\le 79。可以先考虑直接搜正整数拆分得到杨表,然后再把它的轮廓转成一个 11 开头 00 结尾的 0101 串。

    现在我们的问题是要在其开头添加最少个数的 00,结尾添加最少个数的 11,使得存在一种将这个 0101 串转成排列的方案,且这个 0101 串是这个排列的关键串。要求最少个数的原因是,回答询问时要枚举一个 nnn' \le n,使得存在长度为 nn'0101 串且交换方案数为 mm,找到这个 0101 串之后可以在前面补 nnn - n'00 或在后面补 nnn - n'11,再将其转成排列。

    结论:设这个 0101 串逆序对数(即搜出来的杨表格子数)为 kk0101 串长度为 ll,那么要在开头添加 kl+2k - l + 200,在结尾添加 kl+2k - l + 211。将一个 0101 串转化成排列的方法是,设 11 的个数为 tt,从左往右将 11 填成 n,n1,,nt+1n, n - 1, \ldots, n - t + 1,再从左往右将 00 填成 nt,nt1,,1n - t, n - t - 1, \ldots, 1

    注意到只有 2kl+4802k - l + 4 \le 80 的杨表才有用(称其为有效状态),所以边搜边剪枝,可以保证不会搜到无效状态。

    可以使用哈希表存储所有形如 (长度,交换方案数,01 串)(\text{长度}, \text{交换方案数}, \text{01 串}) 的三元组。

    时间复杂度 O((a(n)+q)n)O((a(n) + q)n),其中 a(n)a(n) 为有效状态数,当 n=80n = 80a(n)2×106a(n) \approx 2 \times 10^6

    #include <bits/stdc++.h>
    #include <ext/pb_ds/assoc_container.hpp>
    #define pb emplace_back
    #define fst first
    #define scd second
    #define mkp make_pair
    #define mems(a, x) memset((a), (x), sizeof(a))
    
    using namespace std;
    typedef long long ll;
    typedef double db;
    typedef __int128 lll;
    typedef unsigned long long ull;
    typedef long double ldb;
    typedef pair<ll, ll> pii;
    
    const int maxn = 85;
    const int P = 5000011;
    
    ll n, m, mod, a[maxn], fac[maxn], inv[maxn], b[maxn];
    __gnu_pbds::gp_hash_table<ll, lll> mp[maxn];
    
    void dfs(int s, int lst, ll res, lll x) {
    	if (s) {
    		ll re = res * fac[s] % mod;
    		int t = s - (m + lst) + 2;
    		mp[m + lst + t * 2][re] = (x << t) | ((((lll)1) << t) - 1);
    	}
    	for (int i = lst; s + i <= 79; ++i) {
    		if (2 * (s + i) - (m + 1 + i) + 4 > 80) {
    			continue;
    		}
    		a[++m] = i;
    		ll r = res;
    		for (int j = 1; j <= i; ++j) {
    			r = r * inv[i - j + (++b[j])] % mod;
    		}
    		dfs(s + i, i, r, ((x << (a[m] - a[m - 1])) | ((((lll)1) << (a[m] - a[m - 1])) - 1)) << 1);
    		for (int j = 1; j <= i; ++j) {
    			--b[j];
    		}
    		--m;
    	}
    }
    
    inline void init() {
    	fac[0] = 1;
    	for (int i = 1; i <= 80; ++i) {
    		fac[i] = fac[i - 1] * i % mod;
    	}
    	inv[1] = 1;
    	for (int i = 2; i <= 80; ++i) {
    		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    	}
    	dfs(0, 1, 1, 0);
    }
    
    void solve() {
    	scanf("%lld%lld", &n, &m);
    	if (m == 1) {
    		for (int i = 1; i <= n; ++i) {
    			printf("%d%c", i, " \n"[i == n]);
    		}
    		return;
    	}
    	for (int i = n; i >= 2; --i) {
    		if (mp[i].find(m) != mp[i].end()) {
    			lll x = mp[i][m];
    			ll t = n;
    			for (int j = 1; j <= n; ++j) {
    				if ((x >> (n - j)) & 1) {
    					a[j] = (t--);
    				}
    			}
    			for (int j = 1; j <= n; ++j) {
    				if (((~x) >> (n - j)) & 1) {
    					a[j] = (t--);
    				}
    			}
    			for (int j = 1; j <= n; ++j) {
    				printf("%lld%c", a[j], " \n"[j == n]);
    			}
    			return;
    		}
    	}
    	puts("-1");
    }
    
    int main() {
    	int T = 1;
    	scanf("%d%lld", &T, &mod);
    	init();
    	while (T--) {
    		solve();
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    9812
    时间
    4000ms
    内存
    512MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者