闲话 23.3.17
闲话
今日闲话【碎片】
打每日刷新的副本时可能随机掉落,当且仅当今日有模拟赛时掉落概率不为 0。
怎么这几天的闲话阅读量单调递减呢?
没人想看我的垃圾博客;;
模拟赛
口胡万岁!
T1
考虑把空间和基对应,为方便,我们只计算最简阶梯矩阵。同样的,一组基的张成中最大向量就是这组基的加和,这启发我们作数位 dp。
设 表示填到第 位,基大小为 ,卡上界当 为真。设当前位的上界是 ,考虑是否卡界/添加主元可以得到
总复杂度 。
code
#include <bits/stdc++.h>
using namespace std;
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; while ( T -- )
#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 = 45;
int n, f[N][N][2];
int main() {
iot; cin >> n;
f[30][0][1] = 1;
pre(i,30,1) rep(j,0,30) rep(k,0,1) if (f[i][j][k]) {
int top = k ? (n >> (i - 1) & 1) : 1;
addi(f[i - 1][j][k && 0 == top], (1ll << max(j - 1, 0)) * f[i][j][k] % mod);
if (top == 1) {
addi(f[i - 1][j + 1][k], f[i][j][k]);
if (j > 0) addi(f[i - 1][j][k], (1ll << j - 1) * f[i][j][k] % mod);
}
}
int ans = 0;
rep(i,0,30) addi(ans, f[0][i][0], f[0][i][1]);
cout << ans << '\n';
}
T2
首先有个假做法是点分治并用可撤销并查集拼起来,因为两条链可以拼合。
联通块的充要条件是点集 生成边集 ,因为是树。
考虑把一条 上路径 映射到二维平面上的一个点 。
考虑 的每个元素对这个平面的贡献。对一条边 ,包含它的所有路径组成了 个矩形,因为两个端点的可行区间在 dfn 序上是连续(分段连续)的。对一个点 枚举一下出边,能发现有 个矩形。
因此我们就把这 上每个联通块对链的贡献转化成了矩形上的 次加减,点加边减。统计平面上值为 的点数即可,这可以通过线段树维护最小值和数量,作扫描线得到。
总时间复杂度 。
code
#include <bits/stdc++.h>
using namespace std;
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; while ( T -- )
#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;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, t1, t2, u[N], v[N];
vector<tuple<int, int, int>> vc[N];
ll ans;
vi g[N];
int siz[N], dep[N], dfn[N], stp, st[N][21];
void dfs(int u, int ft) {
siz[u] = 1, dep[u] = dep[ft] + 1, dfn[u] = ++ stp, st[u][0] = ft;
rep(j,1,20) st[u][j] = st[st[u][j - 1]][j - 1];
for (auto v : g[u]) if (v != ft) dfs(v, u), siz[u] += siz[v];
}
inline int getson(int u, int v) {
pre(j,20,0) if (dfn[st[u][j]] > dfn[v]) u = st[u][j];
return u;
}
struct sgt {
#define ls (p << 1)
#define rs (p << 1 | 1)
#define mnv(p) seg[p].mnv
#define cnt(p) seg[p].cnt
#define lzy(p) seg[p].lzy
struct node {
int mnv, cnt, lzy;
} seg[N << 2];
inline void ps_p(int p) {
mnv(p) = min(mnv(ls), mnv(rs));
cnt(p) = cnt(ls) * (mnv(ls) == mnv(p)) + cnt(rs) * (mnv(rs) == mnv(p));
}
inline void ps_d(int p) {
if (!lzy(p)) return;
mnv(ls) += lzy(p), lzy(ls) += lzy(p);
mnv(rs) += lzy(p), lzy(rs) += lzy(p);
lzy(p) = 0;
}
void build(int p, int l, int r) {
mnv(p) = lzy(p) = 0, cnt(p) = 1;
if (l == r) return;
int mid = l + r >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
ps_p(p);
}
void update(int p, int l, int r, int L, int R, int va) {
if (L <= l and r <= R) return static_cast<void>(mnv(p) += va, lzy(p) += va);
ps_d(p); int mid = l + r >> 1;
if (L <= mid) update(ls, l, mid, L, R, va);
if (mid < R) update(rs, mid + 1, r, L, R, va);
ps_p(p);
}
} Tr;
inline void insert(int l1, int r1, int l2, int r2, int va) {
if (l1 <= r1 and l2 <= r2)
vc[l1].eb(l2, r2, va), vc[r1 + 1].eb(l2, r2, -va);
}
signed main() {
iot;
multi {
cin >> n;
rep(i,2,n) cin >> u[i] >> v[i];
rep(i,2,n) cin >> t1 >> t2, g[t1].eb(t2), g[t2].eb(t1);
dfs(1, 0);
rep(u,1,n) {
for (auto v : g[u]) {
if (v == st[u][0]) insert(1, dfn[u] - 1, dfn[u], dfn[u] + siz[u] - 1, 1), insert(dfn[u] + siz[u], n, dfn[u], dfn[u] + siz[u] - 1, 1);
else insert(dfn[v], dfn[v] + siz[v] - 1, 1, dfn[v] - 1, 1), insert(dfn[v], dfn[v] + siz[v] - 1, dfn[v] + siz[v], n, 1);
} insert(dfn[u], dfn[u], 1, n, 1);
}
rep(i,2,n) {
if (dfn[u[i]] < dfn[v[i]]) swap(u[i], v[i]);
if (dfn[u[i]] < dfn[v[i]] + siz[v[i]]) {
int t = getson(u[i], v[i]);
insert(dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, 1, dfn[t] - 1, -1), insert(dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, dfn[t] + siz[t], n, -1);
insert(1, dfn[t] - 1, dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, -1), insert(dfn[t] + siz[t], n, dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, -1);
} else {
insert(dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, dfn[v[i]], dfn[v[i]] + siz[v[i]] - 1, -1);
insert(dfn[v[i]], dfn[v[i]] + siz[v[i]] - 1, dfn[u[i]], dfn[u[i]] + siz[u[i]] - 1, -1);
}
}
Tr.build(1, 1, n);
rep(i,1,n) {
for (auto [l, r, va] : vc[i]) Tr.update(1, 1, n, l, r, va);
// cout << Tr.seg[1].mnv << ' ' << Tr.seg[1].cnt << '\n';
if (Tr.seg[1].mnv == 1) ans += Tr.seg[1].cnt;
}
cout << (ans + n) / 2 << '\n';
rep(i,1,n) g[i].clear(), vc[i].clear(); ans = stp = 0;
}
}
T3
考虑确定一个询问子串的情况。我们可以首先找到所有出现位置 ,随后可以贪心地算出最少加入数。具体地,我们每次找最左边的区间,在他右端点前放一个间隔符,并删除所有包含这间隔符的区间即可。这个上线段树是 的,用 radix sort + umap 是 的。
第二种做法具体如下:
设当前拿到的区间集合是 ,这是左值+右值各 个。首先把 个值 radix sort,从小到大扫值域。如果当前值是左值,把对应区间加入一个 umap。otherwise 给 umap 里所有值打标记并清空 umap,若 umap 清空前不空就答案加 ;如果这个右值对应的左值被打了标记就不清空,略过即可。容易发现这做法是 的。
我们自然想到了根号分治。
对于 的询问,我们发现可能的总串数是 的,可以 地预处理。具体地,我们对每个 的长度 ,找到 个可能的串,每次拿出所有相等的串作如上的贪心。这样总复杂度是 的。
对于 的询问,如果暴力按上面做法做很可能劣化到单次 。但是从上面的想法我们能看出一个性质:若长度 ,清空 umap 的次数不超过 。这告诉我们,上线段树的总时间复杂度是 的。这样我们就只需要快速找到所有右端点。
不难发现这右端点本质上是询问串的 endpos,因此得到原串的后缀链接树后即可在 的复杂度内通过 SAM 上线段树合并的方式得到每个询问串的 endpos 集合。随后在 SAM 上查出串,用线段树做贪心即可。
视 ,总时间复杂度为 ,取 得到时间复杂度 。
code
#include <bits/stdc++.h>
using namespace std;
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; while ( T -- )
#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 = 2e5 + 5, B = 450;
const int inf = 0x3f3f3f3f;
const ll infll = 0x3f3f3f3f3f3f3f3fll;
int n, m, sqr, ret[N][B];
char S[N];
vi vec[N], g[N];
struct sgt {
#define ls(p) seg[p].l
#define rs(p) seg[p].r
int mlc, rt[N];
struct node {
int l, r;
} seg[N << 6];
void update(int& p, int l, int r, int x) {
if (!p) p = ++ mlc;
if (l == r) return;
int mid = l + r >> 1;
if (x <= mid) update(ls(p), l, mid, x);
else update(rs(p), mid + 1, r, x);
}
int merge(int x, int y) {
if (!x or !y) return x | y;
int ret = ++mlc;
ls(ret) = merge(ls(x), ls(y));
rs(ret) = merge(rs(x), rs(y));
return ret;
}
int query(int p, int l, int r, int x) {
if (!p) return 0;
if (l == r) return l;
int mid = l + r >> 1;
if (x > mid) return query(rs(p), mid + 1, r, x);
int ret = query(ls(p), l, mid, x);
if (ret) return ret;
return query(rs(p), mid + 1, r, x);
}
} Tr;
int son[N][26], link[N], len[N], pos[N], mlc = 1, lst = 1;
void extend(int c, int id) {
int now = ++ mlc, p = lst;
len[now] = len[lst] + 1;
while (p and !son[p][c])
son[p][c] = now, p = link[p];
if (p == 0) link[now] = 1;
else {
int q = son[p][c];
if (len[p] + 1 == len[q]) link[now] = q;
else {
int kage = ++ mlc;
len[kage] = len[p] + 1, link[kage] = link[q];
memcpy(son[kage], son[q], sizeof(son[q]));
while (p and son[p][c] == q)
son[p][c] = kage, p = link[p];
link[q] = link[now] = kage;
}
}
pos[id] = lst = now;
}
int fa[N][21];
void dfs(int u) {
rep(i,1,20) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(int v : g[u]) {
dfs(v);
if (len[u] > sqr) Tr.rt[u] = Tr.merge(Tr.rt[u], Tr.rt[v]);
}
}
int find(int u, int d) {
pre(i,20,0) if (len[ fa[u][i] ] >= d) u = fa[u][i]; return u;
}
signed main () {
iot;
cin >> n >> m >> S + 1;
sqr = sqrt(n * log(n));
rep(i,1,n) extend(S[i] - 'a', i);
rep(i,1,mlc) fa[i][0] = link[i], g[link[i]].eb(i);
rep(i,1,n) Tr.update(Tr.rt[pos[i]], 1, n, i);
rep(i,1,n) for(int u = find(pos[i], sqr); u; u = link[u]) vec[u].eb(i);
dfs(1);
rep(i,1,mlc) rep(d,len[link[i]] + 1,min(len[i],sqr)) {
int p = 1;
for(auto it : vec[i]) if (it - d + 1 >= p) ++ret[i][d], p = it;
}
rep(i,1,m) {
int l, r;
cin >> l >> r;
if (l == r) { cout << -1 << "\n"; continue; }
int d = r - l + 1, u = find(pos[r], d);
if (d <= sqr) cout << ret[u][d] << "\n";
else {
int p = 1, ret = 0;
while(p + d - 1 <= n and (p = Tr.query(Tr.rt[u], 1, n, p + d - 1))) ++ ret;
cout << ret << "\n";
}
}
}
杂题
有 张牌,其中一张是王牌。现在你执行 次如下操作:洗牌后查看第一张牌是什么。
令 为洗牌后第一张牌为王牌的次数,现在假设洗牌时 种牌的排列出现的概率均相等,求 的期望值。
。
考虑出现王牌的概率是 ,我们可以简单地枚举出现王牌的次数以及对答案的贡献。答案就是
可以 地求得。
有两个棋子初始点都在坐标 ,两个棋子之间没有区别,总共要进行 次操作,每次操作是如下操作其中之一:
选择一个棋子向前移动一步。
选择位置较后的那个棋子,直接移动到位置较前的那个棋子的位置。
计数 次操作后两个棋子分别在位置 的方案,对 取模。两种方案是相同的,当且仅当两个棋子在每一步的坐标都是相同的(注意不是每一步的操作都相同)。
。
又到了今天的生成函数时间!
什么?你看这题面不像?我也看着不像。
但是确实可以。
约定一个高维生成函数 的第 项记为 。
欸,你别害怕啊!
你看 2. 操作不是很好刻画。那就不刻画了,先找性质。
可以发现虽然 2. 操作执行次数不定,但是其对两个棋子的影响是一样的,这也就让我们可以先只考虑 1. 操作(可以为空),再考虑加一个 操作的影响,用 构造拼起来。
现在只讨论 1. 操作。首先扔到二维平面上,然后就是只能向右向上走,问有多少种走法从 到 且不经过 ;这是由于 在施 2. 操作后都可以到达 ,本质相同。
转二维平面路径为括号序列。
我们可以发现,如果设 O
为合法括号匹配,答案一定形如 O(O(...O(O
,这启发我们首先计数 (O
,再拼出答案。设卡特兰数的 ogf 是 ,并让 表示左括号的占位元, 表示右括号的占位元,则形如 (O
的括号序列的组合类 就能表为 。
设只讨论 1. 操作的操作序列的组合类是 ,则 ,也就是
我们要算答案,首先要用 分别表示两个棋子的坐标,还要加一维 表示用的次数。设答案的组合类是 ,ogf 是 。
可以发现,若记一系列极长的 1. 操作为 ,一次 2. 操作为 ,则最终操作序列一定形如 CSCS...CSC
,并且最后一定停止在 。我们不妨考虑先构造 CS
,再加入一个 C
。为方便,记他们的组合类是 。
CS
就考虑一次 C
到定点 ,再冲到 的贡献。这过程的方案数是 ,对 的贡献都是 ,对 的贡献是 。也就是
C
的话类似,我们只需要考虑前半部分的贡献。这过程的方案数是 ,对 的贡献是 , 的贡献是 ,对 的贡献是 。也就是
我们知道 ,也就是
答案就是 。
考虑分母 的第二维传进去了 ,我们不妨先把他干掉。设 ,我们要的就是
到这一步,考虑对 ,有
我们知道,无论我们要的系数是什么, 都是一个多项式,因此我们想提取的 项一定形如 。所以我们只需要提取出 ,之后对提取 项系数来说这多项式就等价于 了。这自然能证明如上的公式。
上面的结论想用到分式上还得讨论一下。上下两个部分得到的都是一个 的形式,我们需要提取 项的 ,也就需要总共提取一个 ,并在 前留下一个 。具体看如下的推导:
不妨设置哑元 ,令 ,显然有 。有
最后的问题是如何算 ,以及顺便验证 。
考虑 是一个存在复合逆的函数的复合方程的形式,直觉告诉我们可以通过另类拉格朗日反演得到简洁的形式。
到这里就完全解决了本题。注意 。
总时间复杂度 。
以下是博客签名,与正文无关。
请按如下方式引用此页:
本文作者 joke3579,原文链接:https://www.cnblogs.com/joke3579/p/chitchat230317.html。
遵循 CC BY-NC-SA 4.0 协议。
请读者尽量不要在评论区发布与博客内文完全无关的评论,视情况可能删除。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App