标签:fine 元素 最小 namespace algorithm 题意 end 阶乘 +=
给定一个\(n \times n\)的棋盘,要求放\(n\)个棋,使得所有格子都在攻击范围内,且恰好有\(k\)对互相攻击,求方案数对\(998244353\)取模的结果。两个棋互相攻击当且仅当它们在同一行或者同一列,且中间没有其他棋子。\(n \leq 200000\)。
发现每个格子都必须被覆盖,所以每行或者每列应该恰好放一个棋子,假设是每行放一个。我们发现在某行或者某列若有\(x\)个棋子,那么互相攻击棋子对的贡献是\(x-1\)。假设我们一共放\(m\)列,每列放\(a_i\)个,那么总贡献为\(\sum_{i=1}^m a_i -1 =k\),又因为一共有\(n\)个棋子,所以\(\sum_{i=1}^m a_i =n\),差分得\(m=n-k\)。
现在就相当于\(n\)个不同行的棋子,放进\(n-k\)个不同的列,问总方案数。
第二类斯特林数里面的盒子是相同的,但是这题不同,所以要乘上一个阶乘。所以答案是:
由于前面是假设每行放一个,当\(k \neq 0\)的时候每行放一个和每列放一个不等价,答案要乘\(2\)。
#include <stdio.h>
#define it register int
#define ct const int
#define il inline
#define P 998244353
const int N = 1000005;
int n, ans, fac[N], ifac[N], inv[N];
long long K;
il int ksm(it x, it L) { it ans = 1; while (L) L& 1 ? ans = (0ll + ans) * x % P : 0, x = (0ll + x) * x % P, L >>= 1; return ans;}
il int C(ct n, ct m) { return m < 0 || n < m ? 0 : (0ll + fac[n]) * ifac[m] % P * ifac[n - m] % P; }
int main() {
scanf("%d%lld", &n, &K);
if (K >= n) return putchar(‘0‘), 0;
K = n - K; it i;
for (i = 2, fac[0] = fac[1] = ifac[0] = ifac[1] = inv[0] = inv[1] = 1; i <= n; ++i)
fac[i] = (0ll + fac[i - 1]) * i % P, inv[i] = (0ll + inv[P % i]) * (P - P / i) % P, ifac[i] = (0ll + ifac[i - 1]) * inv[i] % P;
for (i = 0; i <= K; ++i) ans = (ans + (0ll + ksm(K - i, n)) * (i & 1 ? P - C(K, i) : C(K, i))) % P;
printf("%d", (K < n ? 2ll : 1ll) * (0ll + ans) * C(n, K) % P);
return 0;
}
给定一个长度为\(n\)的序列\(a\),每次可以选择两个位置 \(i,j(i\neq j)\),然后将\(a_j+=a_i\)并删除\(a_i\)。求将原序列变成严格单调上升的最少操作次数。\(n\leq 15\)。
相当于把序列划分成若干组,每组以某个元素为基础位置,其余元素都累加到这个基础位置上,问最小操作次数。
设 \(f[i][j][s]\) 表示枚举到第 \(i\) 组,第 \(i\) 组以 \(j\) 为基础位置,前 \(i\) 组的状态集合为 \(s\) 的情况下第 \(i\) 组的最小值。
转移时枚举一个不属于任何集合的元素,考虑加到第 \(i\) 个集合里或者新建第 \(i+1\) 个集合,注意第 \(i+1\) 个集合的基础位置的值必须比第\(i\)个大。
输出路径只需要在转移的时候记一下是从哪个状态转移过来的。
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define it register int
#define ct const int
#define il inline
using namespace std;
const int N = 1 << 15, inf = 2139062143;
int T, f[16][16][N], a[N], pw[N], sum[N], n, lim;
pair<int, int> pre[16][16][N];
il void del(it x) { while (x < n) --a[x], ++x; }
il void puo(ct i, ct j, ct s) {
if (!s) return;
ct ss = s ^ pre[i][j][s].first;
for (it x = 0; x < n; ++x)
if (x != j - 1 && (ss & pw[x]))
printf("%d %d\n", a[x], a[j - 1]), del(x);
puo(i - 1, pre[i][j][s].second, pre[i][j][s].first);
}
int main() {
scanf("%d", &T); it i, j, s, ss, tj, k, fl;
for (i = pw[0] = 1; i <= 30; ++i) pw[i] = pw[i - 1] << 1;
while (T--) {
scanf("%d", &n), lim = 1 << n;
for (i = 0; i <= n; ++i) for (j = 0; j <= n; ++j) memset(f[i][j], 127, lim << 2);
for (i = 0; i < lim; ++i) sum[i] = 0;
for (i = 0; i < n; ++i) scanf("%d", &j), sum[pw[i]] = j, a[i] = i + 1;
for (i = 0; i < lim; ++i) sum[i] = sum[i & (i - 1)] + sum[i & (-i)];
for (i = f[0][0][0] = 0, --lim; i < n; ++i)
for (s = 0; s <= lim; ++s)
for (j = 0; j < n; ++j)
if (f[i][j][s] < inf)
for (ss = lim ^ s, k = ss; k; k = (k - 1) & ss)
if (sum[k] > f[i][j][s] && (k >> j))
if (f[i + 1][tj = j + __builtin_ctz(k >> j) + 1][s | k] > sum[k])
f[i + 1][tj][s | k] = sum[k], pre[i + 1][tj][s | k] = make_pair(s, j);
for (i = n, fl = 1; fl && i; --i)
for (j = 1; fl && j <= n; ++j)
if (f[i][j][lim] != inf) printf("%d\n", n - i), puo(i, j, lim), fl = 0;
}
return 0;
}
题解 CF1342E Placing Rooks CF1342F Make It Ascending
标签:fine 元素 最小 namespace algorithm 题意 end 阶乘 +=
原文地址:https://www.cnblogs.com/Kylin-xy/p/CF1342E-CF1342F.html