题解:ABC134F Permutation Oddness
[题目链接](AT_abc134_f [ABC134F] Permutation Oddness - 洛谷)
简单说说题面,问你 \(a\) 是一个长度为 \(n\) 的排列, 问有多少个怪异值,即 \(\sum_{i=1}^{n} |i - a_i| = k\) 的 \(a\)。
对于这种绝对值形式的东西,我们可以考虑拆贡献,其中 \(i\) 或者 \(a_i\) 是较小的时,那么贡献是 -1,否则是 +1。
那么这就是将 \(i\) 与 \(a_i\) 配对。
考虑一个朴素的 DP,\(\displaystyle f_{i, x, y, k}\) 是遍历到第 i 对,前面有 x 个下标没配对,y 个值没配对,已经有 k 个贡献的方案数。
显然的,如果我确定一个值或者下标是向后或向前匹配了,那么他对怪异值的贡献就确定了。
然后我们推导转移时可以发现,如果互相指,那么都不变,而后分析,如果两个都向前或都向后,那么 x,y 均加一或减一,如果一个向前一个向后,x,y 是不变的。
这样 x 和 y 的值就是一样的,我们可以将状态砍掉一维。
转移就很好推了。
#include <bits/stdc++.h>
using namespace std;
const int Mod = 1e9 + 7;
long long f[55][55][5005];
int main() {
	/*
	考虑我们拆贡献,将一个 j 放在 i 上理解为 i -> j 的一条线,那我们从 i 到 i + 1 多出来的贡献,就是在其中画一条线,与那些边的交点个数,
	那我们考虑维护向上向下各有几个 “线头”,每次连边,我令向下是 x, 向上是 y 
	现在我们的状态是 : i,x,y,k 表示前 i 个数,线头 xy 个,和是 k 的方案数 
	考虑上下两个点怎么变,如果互相指,那么都不变,而后分析,
	如果两个向同一个方向指,那么 xy 均加一或减一,如果不同方向指,xy 是不变的 
	这里我们发现,xy 是一样的,所以我们可以将状态砍掉一维 
	*/
	int n, k;
	cin >> n >> k;
	int base = n * n;
	f[0][0][base] = 1;
	for (int i = 1;i <= n;i++) {
		for (int j = 0;j <= i;j++) {
			for (int k = 0;k <= 2 * n * n;k++) {
//				if (i == 1 && j == 2 && k == 11) {
//				}
				// 互相指
				(f[i][j][k] += f[i - 1][j][k]) %= Mod;
				// 都向前,则我现在的线头要少
				if (k - i * 2 >= 0) (f[i][j][k] += f[i - 1][j + 1][k - i * 2] * (j + 1) * (j + 1) % Mod) %= Mod;
				// 都向后,则我现在的线头要多
				if (j && k + i * 2 <= 2 * base) (f[i][j][k] += f[i - 1][j - 1][k + i * 2] % Mod) %= Mod; 
				// 上前下后
				(f[i][j][k] += 2 * j * f[i - 1][j][k] % Mod) %= Mod;
//				if (f[i][j][k]) {
//					cout << i << " " << j << " " << k - base << " " << f[i][j][k] << "\n";
//					if (k >= j) cout << i - 1 << " " << j << " " << k - j - base << ": " << f[i - 1][j][k - j] << " 1\n";
//					if (k >= j + 2) cout << i - 1 << " " << j + 2 << " " << k - j - 2 - base << ": " << f[i - 1][j + 2][k - j - 2] << " 2\n";
//					if (k >= j - 2 && j >= 2) cout << i -1 << " " << j - 2 << " " << k - j + 2 - base << " " << f[i - 1][j - 2][k - j + 2] << " 3\n";
//					if (k >= j) cout << i - 1 << " " << j << " " << k - j - base << ": " << 2 * f[i - 1][j][k - j] << " 4\n";
//					cout << "\n";
//				}
			}
		}
	}
//	cout << f[1][2][11];
	cout << f[n][0][k + base];
	return 0;
}
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号