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,还是比较大的。

相关推荐
6Hzlia3 小时前
【Hot 100 刷题计划】 LeetCode 136. 只出现一次的数字 | C++ 哈希表&异或基础解法
c++·算法·leetcode
MWWZ4 小时前
最近的一些软件更新
opencv·算法·计算机视觉
CoovallyAIHub4 小时前
视频理解新范式:Agent不再被动看视频,LensWalk让它自己决定看哪里
算法·架构·github
汉克老师4 小时前
GESP2024年6月认证C++三级( 第二部分判断题(1-10))
c++·数组·位运算·补码·gesp三级·gesp3级
CoovallyAIHub4 小时前
斯坦福丨AirVLA:将地面机械臂模型迁移至无人机实现空中抓取,成功率从23%提升至50%
算法·架构·github
无限进步_5 小时前
【C++】只出现一次的数字 II:位运算的三种解法深度解析
数据结构·c++·ide·windows·git·算法·leetcode
Takoony5 小时前
GPU 推理并发的本质:从第一性原理到工程实践
算法·gru
小贾要学习5 小时前
【Linux】TCP网络通信编程
linux·服务器·网络·c++·网络协议·tcp/ip
哎嗨人生公众号6 小时前
手写求导公式,让轨迹优化性能飞升,150ms变成9ms
开发语言·c++·算法·机器人·自动驾驶