题解 CF1342E Placing Rooks CF1342F Make It Ascending

CF1342E

题意

给定一个(n imes 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)个不同的列,问总方案数。

第二类斯特林数里面的盒子是相同的,但是这题不同,所以要乘上一个阶乘。所以答案是:

[inom{n}{n-k} egin{Bmatrix} n \n-k end{Bmatrix} (n-k)! \ =inom{n}{n-k} sum_{i=0}^k (-1)^k inom{k}{i} (k-i)^n ]

由于前面是假设每行放一个,当(k eq 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;
}

CF1342F

题意

给定一个长度为(n)的序列(a),每次可以选择两个位置 (i,j(i eq j)),然后将(a_j+=a_i)并删除(a_i)。求将原序列变成严格单调上升的最少操作次数。(nleq 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
", 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 - i), puo(i, j, lim), fl = 0;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Kylin-xy/p/CF1342E-CF1342F.html