闲话 23.3.30
闲话
HE 怎么又 15 个名额
乍一看像个强省
CQ 怎么 B2 = 19.9 B3 = 11
平均分高于全国 94.8 分?
基准分高于全国 50 分?
哦 BJ 和 CQ 都是春季赛
但是 ZJ 还是真nb
明天大概率没有闲话!
所以今天要推歌!
推歌:【洛天依AI】《海市蜃楼》多像个童话,刻画成我眼里的年华
模拟赛
摆!
T1 卷王
考虑差分异或 得到一个序列 a,即 是原序列第 两个位置的异或
第 秒按第 个开关会使得第 秒 两个位置翻转,这变化会保存
可以发现的是,除了 外,所有 的位置都只有在第 的时刻翻转 而 总已经被翻转了
所以如果确定了一个操作到现在的时间,我们可以轻易确定这个状态对现在 a 序列的影响
这样我们不妨设计一种状压 dp 来倒着枚举操作序列
设 表示后 秒内(操作序列长度为 ) 状态是/否可以翻转到全 0
初始 f(0, 00...0) = true;
每次枚举状态 f(t, S) = true,并枚举要加入到操作序列首的操作
可以是不操作,即 f(t + 1, S) = true
然后枚举当前操作位置为 p,我们知道这次操作肯定翻转
并且由于这次操作到当前操作数/时间为 可以知道 也被翻转
这 dp 是 的
我们没必要对每个长度都处理一遍答案
对于一个状态 S,在 S 前面加上任意多的 0 对答案没有影响
因为操作只会向后贡献
所以只需要处理长度为 的即可
并且,打表可以发现最大操作次数不会超过 8,我们能把每两个答案压进一个可见字符
这样代码长度 \le 40k,总复杂度 O(2^n + Tn)(
code
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define inline __attribute__((__gnu_inline__, __always_inline__, __artificial__)) inline
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long;
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
template<typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
template<typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
#define multi int _T_; cin >> _T_; for (int TestNo = 1; TestNo <= _T_; ++ TestNo)
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 20 + 10, M = 1 << 16 | 3;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int V = (1 << 16) - 1, ans[M], a[N], f[17][M];
char ch[N];
signed main(){
f[0][0] = 1;
rep(t,0,15) rep(S,0,V) if (f[t][S]) {
f[t + 1][S] = 1;
rep(p,0,15) {
int Si = S ^ (1 << p);
if (p + t + 1 <= 15) Si ^= 1 << (t + p + 1);
f[t + 1][Si] = 1;
}
}
rep(S,0,V) rep(t,0,15) if (f[t][S]) { ans[S] = t; break; }
// for (int i = 0; i <= V; i += 2) {
// cout << (char)(' ' + ans[i] * 9 + ans[i + 1]);
// }
// for (int i = 0, t = 0; i <= V; i += 2, ++ t) {
// gr[t] -= ' ';
// ans[i] = gr[t] / 9, ans[i + 1] = gr[t] % 9;
// }
multi {
cin >> ch + 1; int top = 0, l = strlen(ch + 1);
rep(i,l+1,16) a[++ top] = 0;
rep(i,1,l) a[++ top] = ch[i] - '0';
pre(i, top, 1) a[i] ^= a[i - 1];
int stat = 0;
rep(i,1,top) stat |= (a[i] << i - 1);
cout << ans[stat] << '\n';
}
}
// while (1) {
// cin >> ch + 1; int pos, l = strlen(ch + 1); cin >> pos;
// rep(p,pos - 1,l) {
// cout << "Dt = " << p - pos + 1 << '\n';
// if (p >= pos) ch[p] ^= 1;
// cout << ch + 1 << '\n';
// rep(i,1,l) a[i] = ch[i] - '0';
// pre(i,l,1) a[i] ^= a[i - 1];
// rep(i,1,l) cout << a[i] << ' ';
// cout << '\n' << '\n';
// }
// }
T2 赢王
首先 可行当且仅当 ,原因显然
考虑对每个 统计合法的 ,这样的 可能有 个,性质不太好
考虑对 作前缀和得到 ,则我们需要的就是 的
先不考虑整体咋算,考虑确定了区间 如何计算贡献,记为
从前往后考虑,对 只有动 可以修改 ,这个操作数是 的
然后从前往后扫,对 作前缀和得到 ,到第 i 个元素时其实
所以对 的答案就是
考虑 是可行子序列,并存在 满足 是可行子序列
设 ,对 作前缀和得到 s,我们还知道
则我们知道答案 就是
这样我们就有了平凡的 做法
首先按 分组,组数是 的
然后我们对每组直接 处理出相邻点间的答案
一段的贡献就是包含他的区间个数,这个平凡算
然后加上没贡献的区间的 即可
大概是 60pts
如果数据水可以多过几个包
然后考虑要算啥
可以发现,如果确定了 ,那这一段区间内不同的 对答案的贡献是确定的。所以我们对值域开桶,维护 的 个数与 ,并需要支持区间查询。
这可以树状数组维护,每次讨论区间贡献即可。由于 范围 ,但可能的值只有 个,我们还需要离散化,每次区间查询时在原范围上做讨论,并映射到离散化区间上。
总时间复杂度 。
code
#include <bits/stdc++.h>
using namespace std;
#define inline __attribute__((__gnu_inline__, __always_inline__, __artificial__)) inline
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long;
using ull = unsigned long long; using db = double; using ld = long double; using lll = __int128_t;
template<typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
template<typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
#define multi int _T_; cin >> _T_; for (int TestNo = 1; TestNo <= _T_; ++ TestNo)
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 1e5 + 10, mod = 998244353;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, k, a[N], b[N], s[N], lsh[N], hrt, lsh_s[N], ans, cnt[N], buk[N];
#define find(u) (lower_bound(lsh + 1, lsh + 1 + hrt, u) - lsh)
#define find2(u) (upper_bound(lsh + 1, lsh + 1 + hrt, u) - lsh - 1)
inline int norm(int x) { x >= mod ? x -= mod : 0; return x; }
struct fenwick {
pii Index[N];
inline void add(int p, int v) {
int va = 1ll * v * lsh[p] % mod;
for (; p <= hrt; p += p & -p) Index[p].first += v, Index[p].second = norm(Index[p].second + va);
}
inline pii query(int p) {
pii ret = { 0, 0 };
for (; p > 0; p ^= p & -p) ret.first = norm(ret.first + Index[p].first), ret.second = norm(ret.second + Index[p].second);
return ret;
}
inline int query(int l, int r, int coef1, int coef2) {
pii lp = query(l - 1), rp = query(r);
rp.first = norm(rp.first - lp.first + mod);
rp.second = norm(rp.second - lp.second + mod);
return (1ll * coef1 * rp.second + 1ll * coef2 * rp.first) % mod;
}
} Tr;
signed main() {
cin >> n >> k;
rep(i,1,n) cin >> a[i], b[i] = a[i] % k, s[i] = (s[i - 1] + b[i]) % k;
rep(i,1,n) lsh[i] = s[i]; sort(lsh + 1, lsh + 1 + n);
hrt = unique(lsh + 1, lsh + 1 + n) - lsh - 1;
rep(i,0,n) lsh_s[i] = find(s[i]), ans = norm(ans + buk[lsh_s[i]]), buk[lsh_s[i]]++;
Tr.add(lsh_s[0], --buk[lsh_s[0]]);
cnt[1] = 1;
rep(i,1,n) {
ans = norm(ans + Tr.query(find(lsh[lsh_s[i]] - k / 2), lsh_s[i], lsh[lsh_s[i]], -1));
ans = norm(ans + Tr.query(find(lsh[lsh_s[i]] + (k + 1) / 2), hrt, lsh[lsh_s[i]] + k, -1));
ans = norm(ans + Tr.query(1, find2(lsh[lsh_s[i]] - k / 2 - 1), k - lsh[lsh_s[i]], 1));
ans = norm(ans + Tr.query(lsh_s[i] + 1, find2(lsh[lsh_s[i]] + (k + 1) / 2 - 1), -lsh[lsh_s[i]], 1));
buk[lsh_s[i]]--;
cnt[lsh_s[i]]++;
int deltnum = buk[lsh_s[i]] - cnt[lsh_s[i]] + 1;
Tr.add(lsh_s[i], deltnum);
} cout << ans << '\n';
}
T3 稳王
什么组合数?
期望回合
= 1 + 第 轮打不死 boss 的概率
= 1 + 拿到的打不死 boss 的手牌是 的概率
所以考虑统计所有打不死 boss 的手牌和概率
顺序没有影响 所以考虑按 S 里有的手牌种类计数
先推个式子:
自然有
-
只有复读
6
答案就是 -
只有火球
每次打 2 点伤害,打 次 boss 就没了
设 m =
答案就是 -
只有毒药
每次打一张毒药 打 次 boss 就没了
答案就是 -
复读 + 火球
只要有火球,那复读 = 火球
全是复读和全是火球的已经统计完了
那答案就是
- 火球 + 毒药
最优策略肯定是第一张打毒药,剩下的毒药伤害是 1,火球伤害是 3
我们给 boss 血量 + 1,钦定第一张打出去的毒药有伤害
设 是给 boss 打了 伤害的概率 答案就是
dp 转移是
这可以矩阵快速幂优化 就是
-
复读 + 毒药
毒药伤害是 1,复读伤害是 2
如上 dp 即可 -
三种都有
仍然是直接维护矩阵快速幂
值得注意的是,最终需要做一些容斥,比方说 5. 需要删掉全是毒药的情况
总时间复杂度 ……吧?
没有代码!摆摆摆!
杂题
给定一棵 个结点的树,每条边 有边权 ,结点度数 就是与 节点相连的边数量。对每个 ,删掉一些边使每个结点的度数不大于 ,求出删掉的边的权值和最小值。
。
大佬们怎么都在 22.10.15 3d 做了这题?
首先考虑确定 咋做。这好像很经典。
首先钦定 为根,并设 表示保留 到父亲的边的情况下 子树合法的最小花费, 表示删掉 到父亲的边的情况下 子树合法的最小花费。
考虑 咋算。首先 dfs,算出子树内信息。设 ,,这样 就是从 里取至多 个,从 里取至少 个; 类似。这可以拿个堆贪心地做,先钦定子树全部选 ,然后用堆放入 做可反悔贪心。
这样的复杂度是单次 的,总复杂度 。
这样的复杂度瓶颈在于我们需要对 个决策点做可反悔贪心,考虑如何减少这个量。
一个立即的思考是按度数从小到大做。也就是说,我们从小到大求 ,每次首先处理 的点 。可以知道这样的点集 在 增大时只会并入新元素,这样我们讨论 时的情况。
这时所有 的点 是需要预先处理掉的。我们知道,这些点总不会在 dfs 时对决策产生贡献,因为在决策这些点时一定不会删周围的边,他们只可能给周围的点新增决策点。考虑对每个点 维护一个全局的集合 ,每次对 点和其儿子做可反悔贪心时集合的初值设为 。这样处理点 时,我们就可以给与 存在 边的所有点 的集合内加入 作为新的决策点。注意处理 时不会对周围点的度数产生影响。
随后我们的树上就有一系列需要 dfs 处理的点和剩下的不需要处理的点了,这些需要 dfs 处理的点形成了一系列连通块,我们对这些连通块分别做如上的 dp 即可。
这时我们的可反悔贪心就需要维护一个集合 ,支持删 内最大值到 、查询 内元素和、插入元素。这可以用对顶堆实现,懒惰删除的复杂度是 单次的。
考虑分析复杂度?我们知道 dfs 中决策点的数量是 的,并且全局决策点也是 的,所以总时间复杂度是 的。
以下是博客签名,与正文无关。
请按如下方式引用此页:
本文作者 joke3579,原文链接:https://www.cnblogs.com/joke3579/p/chitchat230330.html。
遵循 CC BY-NC-SA 4.0 协议。
请读者尽量不要在评论区发布与博客内文完全无关的评论,视情况可能删除。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?