Loading

3.4 CW 模拟赛 赛时记录

前言

跟着策略走, 不贪心, 数据检验
心态好

看题

\(\rm{T1}\)

这个要找很多性质吧

\(\rm{T2}\)

啊啊啊, 这啥啊?

\(\rm{T3}\)

啊啊啊, 这又是啥啊?

\(\rm{T4}\)

啊啊啊, 这又是啥啊
状压领域大师


这场不好打, 先多给点时间想 \(\rm{T1}\) , 后面不太传统, 就多骗一点为主

\(\rm{T1}\)

  • 定义操作 (约束) 和开销 / 收益, 要求最值化开销 / 收益
    • 模拟操作, 找性质
    • 最优化问题的瓶颈, 考虑找最优解的性质来处理
    • 逐元素处理
      • 先找到统一的构造方式
      • 直接处理
      • 推导动态规划
    • 先找到一组简单的合法解, 然后在基础上进行调整, 使其花销更优
  • 求多种方式的贡献和
    • 往往更改贡献主题, 求花费对应的操作方式个数

思路

题意

给定一颗带权树
要求找到 nn 个二元组, 要求每个点在二元组的第一个位置和第二个位置都恰好出现 11 次, 求这 nn 个二元组的最大点对距离

真没啥思路, 先看特殊性质

\(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°270\degree 内角数量
多边形有一车约束不在这里阐述

直觉告诉我们 \(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;
}

尽力了, 真就是太菜了, 也许不适合搞竞赛

posted @ 2025-03-04 11:43  Yorg  阅读(17)  评论(0)    收藏  举报