3.4 CW 模拟赛 赛时记录
前言
跟着策略走, 不贪心, 数据检验
心态好
看题
\(\rm{T1}\)
这个要找很多性质吧
\(\rm{T2}\)
啊啊啊, 这啥啊?
\(\rm{T3}\)
啊啊啊, 这又是啥啊?
\(\rm{T4}\)
啊啊啊, 这又是啥啊
状压领域大师
这场不好打, 先多给点时间想 \(\rm{T1}\) , 后面不太传统, 就多骗一点为主
\(\rm{T1}\)
- 定义操作 (约束) 和开销 / 收益, 要求最值化开销 / 收益
- 模拟操作, 找性质
- 最优化问题的瓶颈, 考虑找最优解的性质来处理
- 逐元素处理
- 先找到统一的构造方式
- 直接处理
- 推导动态规划
- 先找到一组简单的合法解, 然后在基础上进行调整, 使其花销更优
- 求多种方式的贡献和
- 往往更改贡献主题, 求花费对应的操作方式个数
思路
题意
给定一颗带权树
要求找到 个二元组, 要求每个点在二元组的第一个位置和第二个位置都恰好出现 次, 求这 个二元组的最大点对距离
真没啥思路, 先看特殊性质
\(n \leq 20\)
这个可以用状压做
令 \(f_{i, \mathbb{S}}\) 表示对于点 \(i\) , 当前已经连上了 \(\mathbb{S}\) 中的点的最大点权和
分别枚举转移即可, 先预处理任意点对距离
代码
/*状压*/
class subtask1 {
private:
int dis[21][21];
__int128 f[21][(1 << 20)];
std::vector<int> Sp[21];
void init() {
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) dis[i][j] = INF;
memset(f, 0, sizeof f);
for (int i = 1; i <= n; i++) for (auto to : E[i]) dis[i][to.first] = to.second;
for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) dis[i][j] = std::min(dis[i][j], dis[i][k] + dis[k][j]);
for (int S = 0; S < (1 << n); S++) Sp[__builtin_popcount(S)].push_back(S);
for (int i = 1; i <= n; i++) dis[i][i] = 0;
}
public:
void solve() {
init();
for (int i = 0; i < n; i++) {
for (auto S : Sp[i]) {
for (int j = 1; j <= n; j++) {
if ((S >> (j - 1)) & 1) continue;
gtmx(f[i + 1][S | (1 << (j - 1))], f[i][S] + dis[i + 1][j]);
}
}
}
write(f[n][(1 << n) - 1]);
}
} sub1;
菊花图
发现每个外圈点出去进来共有 \(2\sum w\) 的花费
代码
class subtask2 {
private:
public:
void solve() {
write(sigma * 2);
}
} sub2;
链
不太好观察, 先打出暴力之后再看性质
显然性质
代码
class subtask3 {
private:
public:
void solve() {
__int128 ans = 0;
for (int i = 1; i <= n / 2; i++) ans += list[i] * i;
for (int i = n / 2 + 1; i < n; i++) ans += list[i] * (n - i);
write(ans * 2);
}
} sub3;
\(\rm{T2}\)
必须在后面想办法补上 \(\rm{T1}\) 的 \(\textrm{40 pts}\) , 加油
- 解不等式的策略
- 直接硬转, 判断存在性
- 贪心的令不等式的条件最容易成立, 只要有一个成立即可
思路
题意
给定一多边形端点, 求 内角数量
多边形有一车约束不在这里阐述
直觉告诉我们 \(270 \degree\) 的角度是可以通过移动方向判定的, 然而并不行
所以不太可做, 丢了
\(\rm{T3}\)
他敢这么给数据一定有他的道理, 所以尝试一下
对于 \(f(i)\) , 每一对 \(x, y\) 组成的两个端点 \((x, 0), (0, y)\) 可能形成多种平行四边形
我们需要找到 \(\alpha, \beta\) 满足 \((x + \beta) \times (y + \alpha) - xy - \alpha\beta = i\)
也就是说, \(f(i)\) 可以通过枚举 \(x, y \in [1, i)\) , 然后找存在多少对 \(\alpha, \beta\) 满足 \(\alpha x + \beta y = i\)
这个应该稍微可做一点, 先去把 \(\rm{T4}\) 暴力打了
你发现这个是常用的公式
\(\alpha, \beta\) 有通解
设 \(\gcd(x, y) = d\) , 如果 \(d\) 不能整除 \(i\) , 那么无解
否则 \(\alpha = \alpha_0 + (\frac{y}{d}) n, \beta = \beta_0 - (\frac{x}{d})n\)
一点都不好算, 只能先把 \(20\) 打了
这套题理论上应该要过 \(\rm{B}\) 才好说, 但是我不会啊
\(\rm{T4}\)
思路
\(n, m \leq 4\)
直接乱做
两种特殊性质
不同的枚举方法, 直接判断是否可行
代码
#include <bits/stdc++.h>
#define int long long
const int MAXN = 52;
const int MOD = 1e9 + 7;
namespace calc {
int add(int a, int b) { return a + b >= MOD ? a + b - MOD : a + b; }
void addon(int &a, int b) { a = add(a, b); }
} using namespace calc;
int n, m;
int mp[MAXN][MAXN];
bool use[MAXN][MAXN];
int ans = 0;
bool check(int x, int y, int op) {
/*上*/
if (op == 1) {
return (x > 1 && x <= n && y > 1 && y < m && !use[x][y] && !use[x - 1][y] && !use[x][y - 1] && !use[x][y + 1]);
}
/*左*/
if (op == 2) {
return (x > 1 && x < n && y > 1 && y <= m && !use[x][y] && !use[x - 1][y] && !use[x][y - 1] && !use[x + 1][y]);
}
/*下*/
if (op == 3) {
return (x >= 1 && x < n && y > 1 && y < m && !use[x][y] && !use[x + 1][y] && !use[x][y - 1] && !use[x][y + 1]);
}
/*右*/
if (op == 4) {
return (x > 1 && x < n && y >= 1 && y < m && !use[x][y] && !use[x + 1][y] && !use[x - 1][y] && !use[x][y + 1]);
}
}
void draw(int x, int y, int op) {
/*上*/
if (op == 1) {
use[x][y] = true, use[x - 1][y] = true, use[x][y - 1] = true, use[x][y + 1] = true;
}
/*左*/
if (op == 2) {
use[x][y] = true, use[x - 1][y] = true, use[x][y - 1] = true, use[x + 1][y] = true;
}
/*下*/
if (op == 3) {
use[x][y] = true, use[x + 1][y] = true, use[x][y - 1] = true, use[x][y + 1] = true;
}
/*右*/
if (op == 4) {
use[x][y] = true, use[x + 1][y] = true, use[x - 1][y] = true, use[x][y + 1] = true;
}
}
void back(int x, int y, int op) {
/*上*/
if (op == 1) {
use[x][y] = false, use[x - 1][y] = false, use[x][y - 1] = false, use[x][y + 1] = false;
}
/*左*/
if (op == 2) {
use[x][y] = false, use[x - 1][y] = false, use[x][y - 1] = false, use[x + 1][y] = false;
}
/*下*/
if (op == 3) {
use[x][y] = false, use[x + 1][y] = false, use[x][y - 1] = false, use[x][y + 1] = false;
}
/*右*/
if (op == 4) {
use[x][y] = false, use[x + 1][y] = false, use[x - 1][y] = false, use[x][y + 1] = false;
}
}
void dfs1(int x, int y) {
if (x == n + 1) {
addon(ans, 1);
return;
}
if (mp[x][y] == 0) {
if (y == m) dfs1(x + 1, 1);
else dfs1(x, y + 1);
} else if (mp[x][y] == 1) {
for (int i = 1; i <= 4; i++) {
if (check(x, y, i)) {
draw(x, y, i);
if (y == m) dfs1(x + 1, 1);
else dfs1(x, y + 1);
back(x, y, i);
}
}
} else {
if (y == m) dfs1(x + 1, 1);
else dfs1(x, y + 1);
for (int i = 1; i <= 4; i++) {
if (check(x, y, i)) {
draw(x, y, i);
if (y == m) dfs1(x + 1, 1);
else dfs1(x, y + 1);
back(x, y, i);
}
}
}
}
std::vector<std::pair<int, int>> xpos, opos;
void dfs2(int now) {
if (now == opos.size()) {
addon(ans, 1); return;
}
int x = opos[now].first, y = opos[now].second;
for (int i = 1; i <= 4; i++) {
if (check(x, y, i)) {
draw(x, y, i);
dfs2(now + 1);
back(x, y, i);
}
}
}
void dfs3(int now) {
if (now == xpos.size()) {
addon(ans, 1); return;
}
int x = xpos[now].first, y = xpos[now].second;
dfs3(now + 1);
for (int i = 1; i <= 4; i++) {
if (check(x, y, i)) {
draw(x, y, i);
dfs3(now + 1);
back(x, y, i);
}
}
}
signed main() {
scanf("%lld %lld", &n, &m);
bool existx = false, existo = false;
for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) {
char ch; std::cin >> ch;
if (ch == 'o') mp[i][j] = 1, existo = true, opos.push_back({i, j});
else if (ch == '-') mp[i][j] = 0;
else mp[i][j] = 2, existx = true, xpos.push_back({i, j});
}
if (n <= 4 && m <= 4) { dfs1(1, 1); printf("%lld", ans); return 0; }
if (!existx) {
dfs2(0);
printf("%lld", ans);
return 0;
}
dfs3(0);
printf("%lld", ans);
return 0;
}
尽力了, 真就是太菜了, 也许不适合搞竞赛

浙公网安备 33010602011771号