做题记录 // 230214
情人,情人,情人节!
A. #4681. mm 和 tt 吃糖果
http://222.180.160.110:1024/contest/3302/problem/1
不难想到把题目抽象成图论模型……
记得上一次我说这话的时候还是在幻想乡修 wifi。然后抽象完了就寄了。
不妨把某个点和所有位于其右下方的点连边,然后跑一个拓扑排序,对每个结点记录最大值(表示第一次走)与次大值(表示第二次走),然后很玄学地为只最大值加上当前点权……
理所当然地寄了。
不妨参考下面这组 Hack:
Input
3 3
1 2 4
2 3 5
1 3 4
Output
13
原因很简单,这么做的话 \((1,2)\) 既会更新 \((1,3)\),也会更新 \((2,3)\)。
所以我们只能 DP…… 不难想到四维 DP,使用 f[i][j][k][l]
表示第一次走到 \((i,j)\),第二次走到 \((k,l)\) 的最大价值。
这个同时走的思想就很妙。因为只能朝右朝下走,走到某个点所花费的步数是一定的,也就是说不存在第一次花费 \(s\) 步走到 \((x,y)\),第二次需要花费 \((s+1)\) 步的情况。
好妙啊,为什么所有人都认为这个思路转折点很一眼呢???可以退役啦!
namespace XSC062 {
using namespace fastIO;
const int maxn = 25;
int n, k, x, y;
int a[maxn][maxn];
int f[maxn][maxn][maxn][maxn];
inline int max(int x, int y) {
return x > y ? x : y;
}
int main() {
read(n), read(k);
while (k--)
read(x), read(y), read(a[x][y]);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
for (int k = 1; k <= n; ++k) {
for (int l = 1; l <= n; ++l) {
if (i == k && j == l) {
f[i][j][k][l] = max(f[i][j][k][l],
f[i - 1][j][k - 1][l] + a[i][j]);
f[i][j][k][l] = max(f[i][j][k][l],
f[i - 1][j][k][l - 1] + a[i][j]);
f[i][j][k][l] = max(f[i][j][k][l],
f[i][j - 1][k - 1][l] + a[i][j]);
f[i][j][k][l] = max(f[i][j][k][l],
f[i][j - 1][k][l - 1] + a[i][j]);
}
else {
f[i][j][k][l] = max(f[i][j][k][l],
f[i - 1][j][k - 1][l]
+ a[i][j] + a[k][l]);
f[i][j][k][l] = max(f[i][j][k][l],
f[i - 1][j][k][l - 1]
+ a[i][j] + a[k][l]);
f[i][j][k][l] = max(f[i][j][k][l],
f[i][j - 1][k - 1][l]
+ a[i][j] + a[k][l]);
f[i][j][k][l] = max(f[i][j][k][l],
f[i][j - 1][k][l - 1]
+ a[i][j] + a[k][l]);
}
}
}
}
}
print(f[n][n][n][n]);
return 0;
}
} // namespace XSC062
B. mob 的科学麻将
http://222.180.160.110:1024/contest/3302/problem/2
牛逼题!反正我是一辈子想不出来!
std 如下,注释是我打的!
#include <cstdio>
#define int long long
const int mod = 1e9 + 7;
int dp[2][13][5005];
int pv, x, p, v, ans;
int n, m, i, j, k, nw;
signed main() {
scanf("%lld%lld", &n, &m);
pv = m;
// p: m 是 2 的几次方
while ((pv & 1) == 0)
p++, pv >>= 1; // 处理系数
for (i = 1; i <= n; i++) {
nw = !nw; // 滚动
scanf("%lld", &x);
for (j = 0; j <= p; j++) {
for (k = 0; k < (m >> j); k++) {
// 继承
dp[nw][j][k] = dp[!nw][j][k];
v = (k - (x % (m >> j)) + (m >> j))
% (m >> j);
if (j > 0) {
dp[nw][j][k] = (dp[nw][j][k] +
dp[!nw][j - 1][v] + dp[!nw]
[j - 1][v + (m >> j)]) % mod;
}
if (j == 0) {
if (k == x % m) {
dp[nw][j][k] =
(dp[nw][j][k] + 1) % mod;
}
}
if (j == p) {
dp[nw][j][k] = (dp[nw][j][k] +
dp[!nw][j][v]) % mod;
}
}
}
}
for (i = 0; i <= p; i++)
ans = (ans + dp[nw][i][0]) % mod;
printf("%lld\n", ans);
return 0;
}
C. mob 的《麻将与概率系统导论》
http://222.180.160.110:1024/contest/3302/problem/3
std 如下:
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
ll n, i, m, x, y, j, S, high_bit[131072], p[131072], dp[131072];
const ll mod = 998244353;
int main() {
scanf("%lld", &n);
for (i = 0; i < 131072; i++)
for (j = 0; j < 17; j++)
if ((1 << j) & i)
high_bit[i] = 1 << j;
for (i = 1; i <= n; i++) {
scanf("%lld%lld", &x, &y);
p[y] = (p[y] + x) % mod;
}
dp[0] = 1;
for (i = 1; i < 131072; i++) {
S = i - high_bit[i];
for (j = S; j; j = (j - 1) & S) dp[i] = (dp[i] + p[j + high_bit[i]] * dp[i - j - high_bit[i]]) % mod;
dp[i] = (dp[i] + p[high_bit[i]] * dp[i - high_bit[i]]) % mod;
}
scanf("%lld", &m);
for (i = 1; i <= m; i++) {
scanf("%lld", &x);
printf("%lld\n", dp[x]);
}
return 0;
}
—— · EOF · ——
真的什么也不剩啦 😖