2026-04-09~12 hetao1733837 的刷题记录

2026-04-09~12 hetao1733837 的刷题记录

04-09

LGP1941 [NOIP 2014 提高组] 飞扬的小鸟

原题链接:[NOIP 2014 提高组] 飞扬的小鸟

分析

该怎么设状态呢?
DP 从某些角度而言就是说人话。

我们设 d p i , j dp_{i,j} dpi,j 表示走到 x = i , y = j x=i,y=j x=i,y=j 的位置所花费的代价。这个做一下分讨,讨论上一步是自由下降还是点击上升,对着一坨取 min ⁡ \min min 即可。

但是,复杂度是不对的,考虑优化。

发现点击次数有着一定的差的关系。

所以,对于 1 ≤ j < m 1\le j<m 1≤j<m,到达 x = i , y = j x=i,y=j x=i,y=j 的代价为 d p i , j = min ⁡ { d p i − 1 , j − X i − 1 , d p i , j − X i − 1 } dp_{i,j}=\min\{dp_{i-1,j-X_{i-1}},dp_{i,j-X_{i-1}}\} dpi,j=min{dpi−1,j−Xi−1,dpi,j−Xi−1}。

对于 j = m j=m j=m,枚举每个 Y Y Y 即可。

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 10005, M = 1005;
const long long INF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, X[N], Y[N];
int P[N], L[N], H[N];
bool buc[N];
long long dp[N][M], res[N];
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> k;
    for (int i = 0; i < n; i++){
        cin >> X[i] >> Y[i];
    }
    for (int i = 1; i <= k; i++){
        cin >> P[i] >> L[i] >> H[i];
        buc[P[i]] = true;
    }
    int L_pos[N] = {0}, H_pos[N] = {0};
    for (int i = 1; i <= k; i++) {
        L_pos[P[i]] = L[i];
        H_pos[P[i]] = H[i];
    }
    for (int i = 0; i <= m; i++){
        dp[0][i] = 0;
    }
    for (int i = 1; i <= n; i++){
        dp[i][0] = INF;
    }
    for (int i = 1; i <= n; i++){
        res[i] = INF;
        for (int j = 1; j <= m; j++){
            dp[i][j] = INF;
        }
        for (int j = X[i - 1] + 1; j < m; j++){
            dp[i][j] = min(dp[i][j], min(dp[i - 1][j - X[i - 1]], dp[i][j - X[i - 1]]) + 1);
        }
        for (int j = m - X[i - 1]; j <= m; j++){
            dp[i][m] = min(dp[i][m], min(dp[i - 1][j], dp[i][j]) + 1);
        }
        for (int j = 1; j + Y[i - 1] <= m; j++){
            dp[i][j] = min(dp[i][j], dp[i - 1][j + Y[i - 1]]);
        }
        if (buc[i]){
            for (int j = 1; j <= L_pos[i]; j++){
                dp[i][j] = INF;
            }
            for (int j = H_pos[i]; j <= m; j++){
                dp[i][j] = INF;
            }
        }
        for (int j = 1; j <= m; j++){
            res[i] = min(res[i], dp[i][j]);
        }
        if (res[i] == INF){
            int cnt = 0;
            for (int j = 1; j < i; j++){
                if (buc[j])
                    cnt++;
            }
            cout << 0 << '\n';
            cout << cnt;
            return 0;
        }
    }
    cout << 1 << '\n';
    cout << res[n];
    return 0;
}

今天不写了,打会文化吧。

LGP2490 [SDOI2011] 黑白棋

原题链接:[SDOI2011] 黑白棋

分析

这个特殊性质给的非常好, k = 2 k=2 k=2,那结果似乎已经注定了!哦,是一坨组合数的相加。就是使得两个棋子限定的中间区域是个奇数即可,所以,白子放的位置,剩下的里面的一半做一个 ∑ C x 1 \sum{C_{x}^{1}} ∑Cx1。

那么,没有这个限制呢?

先不考虑 d d d 的限制呢?算了,设计一个 DP 吧。

哦,难道他博弈的点在于让己方的棋子剩余奇数个离下一个棋子奇数距离?怎么说呢?就是一个奇偶性的博弈。

怎么写呢?

原来是 K --- N i m K---Nim K---Nim 游戏吗?

🐂牛福!

把黑白棋子中间的距离看作一堆石子,则题目变为取 d d d 石子的 N i m Nim Nim 游戏。

那么自然而然地考虑到异或。

其中有一个结论, S G x = x % ( d + 1 ) SG_{x}=x\%(d+1) SGx=x%(d+1)

设 f i , j f_{i,j} fi,j 表示确定异或和二进制第 1 − i 1−i 1−i 位均为 0 0 0、现有 j j j 个石子的方案数。

转移为 f i , j = f i , j × C n − j − k / 2 k / 2 f_{i,j}=f_{i,j}\times C_{n-j-k/2}^{k/2} fi,j=fi,j×Cn−j−k/2k/2

正解

cpp 复制代码
#include <bits/stdc++.h>
#define mod 1000000007
using namespace std;
const int N = 10005;
int n, k, d;
int C[N][205], dp[20][N];
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> k >> d;
    C[0][0] = 1;
    for (int i = 1; i <= n; i++){
        C[i][0] = 1;
        for (int j = 1; j <= 200; j++){
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
    dp[0][0] = 1;
    for (int i = 0; i <= 17; i++){
        for (int j = 0; j <= n - k; j++){
            if (!dp[i][j]) 
                continue;
            for (int x = 0; ; x++){
                int add = (1 << i) * x * (d + 1);
                if (j + add > n - k) 
                    break;
                if (x * (d + 1) > k / 2) 
                    break;
                dp[i + 1][j + add] = (dp[i + 1][j + add] + 1LL * dp[i][j] * C[k / 2][x * (d + 1)] % mod) % mod;
            }
        }
    }
    int ans = 0;
    for (int i = 0; i <= n - k; i++){
        ans = (ans + 1LL * dp[17][i] * C[n - i - k / 2][k / 2] % mod) % mod;
    }
    cout << (C[n][k] - ans + mod) % mod;
    return 0;
}

04-12

LGP7350 「MCOI-04」Dream and Strings

原题链接:「MCOI-04」Dream and Strings

分析

让我卡 Hash 是啥阴?

毫无思路啊......只有一个 O ( 26 ∣ S ∣ ) O({26}^{|S|}) O(26∣S∣) 的暴力。我觉得应该从这个东西怎么算出来的入手。

我毫无注意力啊/ll

T 的前 ∣ S ∣ − 50 |S|-50 ∣S∣−50 位全部变成与 S 一样的,然后再去构造。

但是,第一,这个 50 怎么来的?第二,怎么构造后续的?

合着这个 50 就是个题目性质?那有点......好吧,OI 确实可以这样。

嗯......怎么说呢?就是要构造一个长度为 50 50 50 的字符串,使得这个串与 A 原来的后 50 50 50 为不相同,但哈希值相同。

显然,这 50 个东西可以使用取值位于 [ 0 , 25 ] [0,25] [0,25] 的序列,进一步表示,会发现可以把原序列初始为一个任意字符的东西,然后,通过 & \& & 进行位运算上的改变,控制 ASCII ⁡ \operatorname{ASCII} ASCII 的增减,故,只需枚举一个 0 / 1 0/1 0/1 序列即可。

我不理解/ll

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
const unsigned long long sta = (1ull << 50) - 1;
int base, mod, base_pow[N];
string s;
map<unsigned long long, unsigned long long> buc;
int target = 0, cur;
int len;
mt19937_64 rnd;
void print(unsigned long long x, unsigned long long y){
    for (int i = 0; i < len - 50; i++){
        cout << s[i];
    }
    for (int i = 49; i >= 0; i--){
        cout << (char)('a' + ((x >> i) & 1) + ((y >> i) & 1));
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> base >> mod;
    cin >> s;
    len = s.length();
    base_pow[0] = 1;
    for (int i = 1; i < N; i++){
        base_pow[i] = 1ll * base_pow[i - 1] * base % mod;
    }
    target = 0;
    for (int i = len - 1; i >= len - 50; i--){
        target = (target + 1ll * (s[i] - 'a') * base_pow[len - 1 - i] % mod) % mod;
    }
    while (true){
        unsigned long long tmp = rnd() & sta;
        cur = 0;
        for (int i = 0; i < 50; i++){
            cur = (cur + 1ll * ((tmp >> i) & 1) * base_pow[i] % mod) % mod;
        }
        buc[cur] = tmp;
        if (buc.count((target - cur + mod) % mod)){
            print(tmp, buc[(target - cur + mod) % mod]);
            return 0;
        }
    }
    return 0;
}

不是假设初始全是 aHack 了吗?哦,没有什么问题,我理解错了。

那么,tmp 应该就是随出来的 0/1 序列,所以,成功概率 1 m o d \frac{1}{mod} mod1,还是比较大的。

相关推荐
小poop2 小时前
string 类从入门到深入
c++
眠りたいです3 小时前
现代C++:C++14中的新语言特性和库特性
c语言·开发语言·c++
Black蜡笔小新3 小时前
自动化AI算法训练服务器DLTM助力医学影像分析进入AI智能分析新时代
人工智能·算法·自动化
手写码匠4 小时前
深入解析大模型架构之争:全能通用模型 vs 领域专精模型
人工智能·深度学习·算法·aigc
浅念-4 小时前
LeetCode 回溯算法题——综合练习
数据结构·c++·算法·leetcode·职场和发展·深度优先·dfs
列星随旋5 小时前
线段树和树状数组的学习
学习·算法
楼田莉子6 小时前
C++17新特性:__had_include/属性/求值顺序规则
开发语言·c++·后端
全糖可乐气泡水7 小时前
Codex适配国产信创环境安装部署与技术适配全解析
开发语言·git·python·算法·百度
h_a_o777oah7 小时前
状态机+划分型 DP :深度解析K-划分问题下 DP 状态的转移逻辑(洛谷P2679 P2331 附C++代码)
c++·算法·动态规划·acm·状态机dp·划分型dp·滚动数组优化