信友队 2024 暑假集训 · 壹
Day 0
Day 1
早上入营测,T1 脑子糊涂写错细节。总共挂了 \(100^+\) 分(only \(10\)pts)
例题
-
P1842
邻项交换法考虑。
-
反悔贪心。
-
邻项交换。
-
如果不太好确定当前干嘛,适合反悔。
增加卖油操作,可以把比当前加油站贵的,剩下的油原价卖了(不用管卖给谁)。路上优先烧便宜的油。
-
\(n \le 10^5\)
课后作业
Day 2
例题
模拟
构造
-
构造一个 \(0 \sim n - 1\) 的排列 \(A\),使
\[A_i \oplus i = A_i + i \]注意到对于 \(2^k \le n\) 的最大的 \(k\),\([2^k + 1, n]\) 数位取反后可以和……交换啊什么的。递归进行。
Day 3
lxl 毒瘤!!!
写点新奇的。
笛卡尔树
建树
找出区间最大值,把区间分为两段。每段的最大值作为父亲,向后二者连边。
应用
所有区间 + 区间 \(\max\)
注意到笛卡尔树的每个结点的左右子树合起来就是其为区间 \(\max\) 的区间。(左子树一个左端点,右子树)
所以我们可以以此应用笛卡尔树。
- 写题时补上例题
Day 4
@p_b_p_b !!!
搜索。确定搜索顺序,找到合理有用剪枝。
随堂练习 T2 黑,调了好久终于过了!
Day 5
MST.
例题
-
输出每条边必选时的 MST 权值。
-
简单题。
-
从小到大加边,像 Kruskal 一样维护连通块。连一条边的贡献是左右连通块大小与可选边权数之积。这个 trick 似乎很有用。
-
#3004. Inheritance
-
给一个边权互不相同的图,进行 \(K\) 次删去最大生成森林的操作。对于每条边,求出它在第几轮被删除。
-
图分为 \(K\) 层,分 \(K\) 层试图从大到小加边。每层图用并查集维护连通性。
-
判断某条边在第几层加的时候,二分。
-
这是因为,两个点如果在第 \(A\) 张图联通,在 \(A-1\) 层也是联通的。
-
-
「NOI2018」归程
-
-
考虑 \(2\) 个点时的答案:MST 上的路径最大值。
-
推广:相邻点之间的路径最大值 的最大值。
-
直接预处理,线段树维护即可。
-
-
-
有异或,想到 Trie。
-
那么我们就是在 Trie 上模拟 Kruskal 的选边过程。
-
肯定优先选择 \(\operatorname{LCA}(u, v)\) 深度大的连边。如果要合并 Trie 上 \(2\) 棵子树,那就左右子树各选一个,使其异或和最小。可以启发式合并。
-
-
不知名题目
-
一个长度为 \(N\) 的 01 串 \(S\),有 \(M\) 种操作 \((C_i, L_i, R_i)\),表示可以用 \(C_i\) 的代价把 \(S_l, \dots, S_r\) 取反。求可以把 \(S\) 修改为任意长为 \(N\) 的 01 串的最小代价。
-
和 CF1120D 很像。
-
先把序列取反改成差分数组两点修改。
-
然后就是 1120D 的套路:\(L\) 到 \(R+1\) 连权值为 \(C\) 的边。跑 MST。
-
中午
和 xhx,hyn 和 wjk 在寝室狂玩。
玩什么?
——行李箱“冰壶”,启动!
Day 6 休息日
把昨天所有的题 AK 了。
CF888G 可真牛啊,写发题解。
Day 7
例题
Topologic Sorting
-
时光倒流,最大难点。
-
-
\(ab=c^k\) 等价于 \(s_a + s_b \equiv 0 (\bmod \ k)\)
-
我们就用这个性质寻找一条边可以匹配的边,用 map,Hash table 这类东西存即可。拓扑。
-
-
P3243
Hash
讲解
-
字符串 Hash
-
集合 Hash
-
常用写法:\(H(A) = \sum_{x \in A} g(x) \bmod 2^{64}\)
-
其中 \(g(x)\) 是个随机的函数。当然 \(x\) 一样时 \(g(x)\) 也是一样的。
-
给个例子(只适用于 \(x\) 的值域比较小的情况):
struct Hash { vector <ull> g; Hash(int n) : g(n) { static mt19937_64 rnd(809729); for (ull& i : g) i = rnd(); } ull operator()(const vi a) const { ull ret(0); for (int& i : a) ret += g[i]; // for (int i = 0; i < s.size(); i++) // ret += g[i] * a[i]; // 注释的写法有问题: // 1. 集合的元素是无序的 // 2. 乘法一个很不好的地方就是更容易产生偶数 return ret; } } hs; // 调用 hs(vector < int >)
- Another Example(\(x\) 可以随便)
const ull mask = std::chrono::steady_clock::now().time_since_epoch().count(); ull shift(ull x) { // another g(x) x ^= mask; x ^= x << 13; x ^= x >> 7; x ^= x << 17; x ^= mask; return x; }
-
-
树哈希
-
\(f(u) = 1 + \sum_{v \in subtree_u} g(f(v))\)
-
-
最小表示法什么的,没听懂
例题
-
- 树哈希模板题。
-
- 简单题
-
-
太妙了!
-
首先我们转化一下,找 \(len = 3\) 的等差数列即可。
-
然后,如果 \(a_i\) 是中间那个,则需要 \(\exist k\),满足 \(a_i + k\) 和 \(a_i - k\) 在排列中,在 \(a_i\) 的两侧。
-
这样,我们就需要检测是否存在 \(k\),或什么时候 \(k\) 不存在。首先扫描 \(A_1 \sim A_n\),维护 \(B\),\(B_x=1\) 表示在排列中 \(x\) 在 \(A_i\) 的左边,\(B_x = 0\) 反之。如果 \(B\) 以 \(A_i\) 为中心回文,则 \(k\) 不存在。
-
-
-
simple,
set
常数比map
小很多,尽量不用map
。 -
用了
map
与unordered_map
,被迫卡了几小时的常(换set
才过)。
-
Day 8
啊?时间过半了?
例题
强连通分量
-
缩点模板题
-
又一个缩点模板题
割点与桥
讲无聊内容好多。。。
圆方树也可以建单向边,就是 割点 - 方点,方点 - 另外点。
-
- 板子题,边双缩点找直径。
-
-
不停边双缩点,缩点后建虚树,虚树的直径两端连边。(这是构造方法)
-
答案是缩点后叶子的数量除以 \(2\) 向上取整。
-
-
- 圆方树 + 树剖,板子题。
随堂练习全做过,2min AK。
课后练习
Day 9
讲课内容
-
质数数量规模 \(\pi(n) \approx n / \ln n\)。
-
唯一分解定理
\[n = \prod p_i ^ {s_i} \]且分解至多 \(O(\log n)\) 项。
-
因数个数
\[\prod_i (s_i + 1) \]
-
-
gcd, lcm
本质就是唯一分解的指数取 \(\max\) 和 \(\min\)。因此 \((n, m) \times [n, m] = nm\)。
-
筛子
-
埃氏筛
时间复杂度 \(O(n \ln \ln n)\),证明略,希望你记住了。
-
欧拉筛
运行效率和埃氏筛差不多,但用处多。
-
-
数论函数,积性函数
-
\(\mu(n), \varphi(n), \epsilon(n) = 1, id(n) = n, d(n)\)
-
积性函数,都可以用筛法计算。
-
对于完全积性函数,\(f(p^2) = f(p)^2\)。
-
线性筛求 \(\varphi\), \(\mu\)
-
\(\sum_{d | n} \varphi(d) = n\),由此我们可以得到一个 \(O(n \log n)\) 的简单方法:
for (int i = 1; i <= n; i++) phi[i] = i; for (int i = 1; i <= n; i++) for (int j = 2 * i; j <= n; j += i) f[j] -= f[i];
-
一些公式
\[\because \varphi(p^k) = p^k - p^{k - 1} \]\[\therefore \varphi(n) = n \prod \frac{p_i - 1}{p_i} \]\[\sum_{d | n} \mu(d) = [n = 1] \]最后一个式子可以用二项式定理与 \(\mu\) 的定义证明。
-
例题
-
dp, 考虑将 \(a\) 放在开头的最大值,\(dp_1 = n\)。
\[dp_a = \max_{b | a} \{dp_b + (a - b) \times \sum_{i=1}^n [a |a_i]\} \]复杂度 \(O(n \ln n)\)。
-
发现以前交错代码了 qwq
- 莫比乌斯反演的思路:要变出 \([n = 1]\) 的形式,然后化为 \(\sum_{d|n} \mu(d)\)。化
很久几下,答案:
\[\sum_{p \in P} \sum_{k = 1} \mu(k) \lfloor n / pk \rfloor \lfloor m / pk \rfloor \]求的复杂度是调和级数的。
实际上是推到一半发现了 \(\varphi\),直接随便做。
- 莫比乌斯反演的思路:要变出 \([n = 1]\) 的形式,然后化为 \(\sum_{d|n} \mu(d)\)。化
-
求 \(\sum_{i = 1}^n i^k\),\(n, k \le 10^7\)。
注意到 \(id_k(n) = n^k\) 是(完全)积性函数,直接筛就行。
-
区间加区间 \(\gcd\) 查询
-
首先差分,由于 \(\gcd(a, b) = \gcd(a, b - a)\),所以差分序列的区间 \(\gcd\) 与原序列是一样的。
-
然后可以考虑单点修改区间查询,\(O(n \log n \log a)\),
也许不能通过? -
然而过了???
-
注意到,区间 \(\gcd\) 暴力求是 \(O(n + \log a)\) 的。所以询问与
pushup
就是求 \(\log n\) 的数的 \(\gcd\),复杂度 \(O(\log n + \log a)\)。总复杂度 \(O(m(\log n + \log a))\)。 -
附注:ST 表预处理 \(\gcd\) 也是 \(O(n(\log n + \log a))\) 的。
-
电脑坏了,cnblogs 密码忘记了 qwq
07/17
学校什么 ** 电脑,关机后上午笔记丢了。
同时讲了 07/17 和 07/18 的内容,数论讲完了。
稍微补一点笔记,直接内容例题结合了。
同余最短路
我是大鸽子。
例题
-
跳楼机
-
Roger 买鸭子
07/18 模拟赛
%UR,A 没切,其余 \(3\) 题全暴力,可以退役惹。
总分 \(80\),至少比上次高 ,还是宿舍 rk. 1 呢。
题解
A. WTP 的大洗牌
题意:求 \(x,y\),使 \(x^2 + y^2 \equiv \prod_{i = 1}^n a_i^2 + b_i^2 (\bmod 2^{64})\)。
\(n \le 10^5, x, y, \le 2^{64}\)
\(10000ms\) 的时限纯诈骗,正解是 \({O(n)}\) 的。
我们可以采用推广法。其实是看到 \(n=2\) 的部分分。先考虑 \(n=2\) 的情况。
根据这个,我们可以轻松构造出一组解,而会了 \(n=2\) 我们很快能推广到 \(n\) 的普遍情况。
Code
#include <bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
#define L(i, j, k) for (int i = j; i <= int(k); i++)
#define R(i, j, k) for (int i = j; i >= int(k); i--)
#define qio() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
const int N = 5e5 + 5;
int n;
ull a[N], b[N];
int main() {
qio();
cin >> n;
L(i, 1, n)
cin >> a[i];
L(i, 1, n)
cin >> b[i];
ull a1 = a[1], b1 = b[1];
L(i, 2, n) {
ull aa = a1, bb = b1;
a1 = aa * a[i] + bb * b[i];
b1 = aa * b[i] - bb * a[i];
}
return cout << a1 << ' ' << b1 << '\n', 0;
}
B. 第一题
给一个字符串 \(s\),对于 \(i = 1 \dots |s|\),进行如下操作:
如果 \(t\) 是空串,则 \(t = s_is_i\)。
否则,交换 \(t_2, t_3\),\(t_4, t_5\)……并将 \(s_i\) 插入至 \(t\) 的第二、倒数第二位。
每次操作后,求 \(t\) 的所有前缀偶回文串的哈希值
\[\sum_{i = 1}^{|s|} (\operatorname{ASCLL}(t_i) - \operatorname{ASCLL}(a))131^{|s|-i} \bmod 2^{64} \]强制在线。
首先,这个操作很神秘,我们不知道它在做什么,自然想不出它的性质。因此我们先模拟一下这个操作。
直接拿几个字符做例子太累了,我们直接用 12345678
。
模拟后,我们可以发现,字符的顺序变成了 1827364554637281
。把这个串写出来后我们自然发现:
- 奇数位是原串,偶数位是原串的反串。
这个性质很特殊,我们继续观察。
一个字符串是偶回文串,为什么是偶?我们考虑偶回文串。
以 182736
为例,如果它是回文串,则 123 = 678
。因此我们又发现,偶回文串一定是原串的 Border。
于是我们就可以这样:维护 Hash,再求出后缀树上到根的 Hash 和。(什么玩意儿是人话吗,看 Code 吧)
注意细节,Hash 时用到了 \(bp_{2 |s| - 2}\),所以数组要开大。(本地测下极限数据即可发现)
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vi = vector < int >;
using ull = unsigned long long;
#define pb emplace_back
#define L(i, j, k) for (int i = j; i <= int(k); i++)
#define R(i, j, k) for (int i = j; i >= int(k); i--)
#define qio() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
const int N = 5e6 + 5;
const int base = 131;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
string s;
int k, nxt[N];
ull bp[N << 1], ans[N]; // 数组忘开大,爆零两行泪
void output() {
ull out(0);
L(i, 1, s.size() - 1) {
out ^= ans[i];
if (i % k == 0) {
cout << out << '\n';
out = 0;
}
}
}
char online(char ch, ull last) {
return char(last % 26 + (ch - 'a')) % 26 + 'a';
}
int val(char ch) {
return ch - 'a';
}
int solve() {
bp[0] = 1;
L(i, 1, 2 * s.size() - 1) // 我们之后用到了 bp[2 * i - 2]
bp[i] = bp[i - 1] * base;
ull hs(0), conv_hs(0);
for (int i = 1, k = 0; i < (int)s.size(); i++) {
s[i] = online(s[i], ans[i - 1]);
hs *= base, hs += val(s[i]), hs *= base; // Calc Hash
conv_hs += val(s[i]) * bp[2 * i - 2];
// printf("%llu (i = %d)\n", hs + conv_hs, i);
if (i == 1) { // Special Judge
ans[i] = hs + conv_hs;
continue;
}
while (k && s[i] != s[k + 1]) // KMP
k = nxt[k];
nxt[i] = k += s[k + 1] == s[i];
ans[i] = ans[nxt[i]] + hs + conv_hs; // Calc ans
}
return output(), 0;
}
int main() {
freopen("easy.in", "r", stdin);
freopen("easy.out", "w", stdout);
qio();
cin >> s >> k;
s = " " + s;
return solve();
}
C. WTP 的通缉
给一棵有边权的树,\(q\) 次询问求编号在 \([l_1,r_1], [l_2, r_2]\) 之间的点各选一个,求最大距离。
连续区间,想到线段树;选点集里最大距离,就是广义直径。
所以这题其实是很板的一个题,\([l, r]\) 的直径可以线段树维护,因为两个点集合并后,直径的端点一定是原直径的端点。于是我们进行一个线段树的查询即可。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
#define pb emplace_back
#define me(a, b) memset(a, b, sizeof(a))
#define L(i, j, k) for (int i = j; i <= int(k); i++)
#define R(i, j, k) for (int i = j; i >= int(k); i--)
namespace io {
int read() {
int res(0), f(1);
char ch(getchar());
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
res = res * 10 + (ch ^ '0');
ch = getchar();
}
return res * f;
}
}
using io::read;
const int N = 1e5 + 5;
const int LG = 18;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
struct edge {
int v, w;
};
int n, m;
ve G[N];
namespace LCA {
int dfc, dfn[N], dep[N], rmq[LG][N];
ll edis[N];
int get(int x, int y) {
return dfn[x] < dfn[y] ? x : y;
}
void dfs(int u, int fa) {
rmq[0][dfn[u] = ++dfc] = fa;
for (auto e : G[u])
if (e.v != fa) {
edis[e.v] = edis[u] + e.w;
dep[e.v] = dep[u] + 1;
dfs(e.v, u);
}
}
void init() {
dfs(1, 0);
for (int i = 1; i <= __lg(n); i++)
for (int j = 1; j + (1 << i) - 1 <= n; j++)
rmq[i][j] = get(rmq[i - 1][j], rmq[i - 1][j + (1 <<(i - 1))]);
}
int lca(int u, int v) {
if (u == v)
return u;
if ((u = dfn[u]) > (v = dfn[v]))
swap(u, v);
int d = __lg(v - u++);
return get(rmq[d][u], rmq[d][v - (1 << d) + 1]);
}
ll dis(int u, int v) {
if (!u || !v)
return -INF;
int l = lca(u, v);
return edis[u] + edis[v] - 2 * edis[l];
}
} using LCA::dis;
struct chain {
int u, v;
chain() {
u = v = 0;
}
chain(int x, int y) {
u = x, v = y;
}
};
chain merge(chain a, chain b) {
int u1 = a.u, v1 = a.v, u2 = b.u, v2 = b.v;
if (!u1 || !v1)
return b;
if (!u2 || !v2)
return a;
ll mx = max({dis(u1, v1), dis(u1, v2), dis(u2, v1), dis(u2, v2), dis(u1, u2), dis(v1, v2)});
if (mx == dis(u1, v1))
return chain(u1, v1);
if (mx == dis(u1, v2))
return chain(u1, v2);
if (mx == dis(u2, v1))
return chain(u2, v1);
if (mx == dis(u2, v2))
return chain(u2, v2);
if (mx == dis(v1, v2))
return chain(v1, v2);
if (mx == dis(u1, u2))
return chain(u1, u2);
return chain(0, 0);
}
struct sgt {
chain ch[N << 2];
sgt() {}
#define ls(u) (u << 1)
#define rs(u) (u << 1 | 1)
void pushup(int u) {
ch[u] = merge(ch[ls(u)], ch[rs(u)]);
}
void build(int s, int t, int u) {
if (s == t)
return (void)(ch[u] = chain(s, t));
int mid = (s + t) >> 1;
build(s, mid, ls(u));
build(mid + 1, t, rs(u));
pushup(u);
}
chain query(int s, int t, int l, int r, int u) {
if (l <= s && t <= r)
return ch[u];
int mid = (s + t) >> 1;
chain ret = chain(0, 0);
if (mid >= l)
ret = query(s, mid, l, r, ls(u));
if (mid < r)
ret = merge(ret, query(mid + 1, t, l, r, rs(u)));
return ret;
}
} tr;
int main() {
n = read(), m = read();
for (int i = 1, u, v, w; i < n; i++) {
u = read(), v = read(), w = read();
G[u].push_back({v, w});
G[v].push_back({u, w});
}
LCA::init();
tr.build(1, n, 1);
while (m--) {
int l1 = read(), r1 = read(), l2 = read(), r2 = read();
auto rec1 = tr.query(1, n, l1, r1, 1),
rec2 = tr.query(1, n, l2, r2, 1);
int u1 = rec1.u, v1 = rec1.v,
u2 = rec2.u, v2 = rec2.v;
printf("%lld\n", max({dis(u1, u2), dis(u1, v2), dis(v1, u2), dis(v1, v2)}));
}
return 0;
}
D. 蹦蹦炸弹
一张图,\(m\) 次连边,形式为 \(x, y, l, w\),表示对于 \(i \in Z^+ \cap [0, l)\),\(x + i\) 与 \(y + i\) 之间连一条边权为 \(w\) 的边。求 MST。
其实这题是原:Range Parallel Unionfind
老师:我一眼就切了。可是……
我们考虑 sort 后加边,并查集维护连通性(因为 Kruskal 就是这么做的),但如何并查集?
其实区间连边,让我们想到了拆区间操作,线段树优化建图不也是这样吗?
于是我们可以维护 \(O(\log n)\) 层并查集,第 \(i\) 层并查集中,\(u,v\) 连通表示 \([u, u + 2^i-1),[v, v + 2^i - 1)\) 连通。
高层的并查集要把连通性下推到低层,看起来很暴力,但很显然一个并查集最多连边 \(O(n)\) 次,因此总复杂度是 \(O(n \log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
using LL = __int128;
using ll = long long;
using ldb = long double;
using vi = vector < int >;
using ull = unsigned long long;
#define pb emplace_back
#define ve vector < edge >
#define me(a, b) memset(a, b, sizeof(a))
#define L(i, j, k) for (int i = j; i <= int(k); i++)
#define R(i, j, k) for (int i = j; i >= int(k); i--)
#define qio() ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
namespace io {
int read() {
int res(0), f(1);
char ch(getchar());
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
res = res * 10 + (ch ^ '0');
ch = getchar();
}
return res * f;
}
}
using io::read;
namespace Dbg {
template < class T >
void dbg(char *f, T t) {
cerr << f << '=' << t << endl;
}
template < class T, class ...Ar >
void dbg(char *f, T x, Ar... y) {
while (*f != ',')
cerr << *f++;
cerr << '=' << x << ',';
dbg(f + 1, y...);
}
}
#define gdb(...) cerr << "gdb in line " <<__LINE__ << ": ", Dbg::dbg((char*)#__VA_ARGS__,__VA_ARGS__);
const int N = 1e5 + 5;
const int M = 5e5 + 5;
const int LG = 18;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
struct connect {
int x, y, l, w;
void input() {
x = read(), y = read(), l = read(), w = read();
}
} E[M];
int n, m;
struct dsu {
vi fa;
dsu() {}
void init(int n) {
fa.reserve(n + 1);
for (int i = 1; i <= n; i++)
fa[i] = i;
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool unite(int x, int y) {
if ((x = find(x)) == (y = find(y)))
return 0;
return fa[x] = y, 1;
}
};
int main() {
n = read(), m = read();
L(i, 1, m)
E[i].input();
sort(E + 1, E + m + 1, [](connect a, connect b) {
return a.w < b.w;
});
ll ans = 0;
vector < dsu > G(__lg(n) + 1);
for (int i = 0; i <= __lg(n); i++)
G[i].init(n);
function < void(int, int, int, int) > merge = [&](int x, int y, int dep, int w) {
// printf("merge(%d, %d, %d, %d)\n", x, y, dep, w);
if (!G[dep].unite(x, y))
return;
if (!dep) {
ans += w;
return;
}
merge(x, y, dep - 1, w);
merge(x + (1 << dep - 1), y + (1 << dep - 1), dep - 1, w);
};
L(i, 1, m) {
auto e = E[i];
int x = e.x, y = e.y, l = e.l, w = e.w;
for (int bit = 0; l; l >>= 1, bit++)
if (l & 1) {
merge(x, y, bit, w);
x += 1 << bit, y += 1 << bit;
}
}
return printf("%lld\n", ans), 0;
}
比赛总结
反正信友队那里写了,直接 copy 过来。
考试过程
我用约 15min 通读了题目
发现 D 是最小生成树,没细看,似乎可以线段树优化建图
然后开 A,瞪了几下没什么结果,也没有推式子、找性质,突然看到
A 的时限是 10000ms,顿时很惊讶,以为正解是暴力
大约 45min 的时候给 A 写完了暴力(完全错掉)
之后观察了下别的题,用 30min 先写了 C 的 30pts 暴力
之后我错认为我发现了 D 的并查集正解(假了,和正解还有差距)
于是开始写。写了几下发现大样例过不去,于是花了 30min 对拍
对拍过程中发现题读错,假完了,只好交一发 40pts 的暴力.这时 10:48
现在唯一能拿的是 B 的部分分,我写了个巨难写的暴力(真的比正解还难写),11:40 写完,发现爆零了。
最后我发现我代码交错了,还有 freopen 没删的情况,救了
70pts
知识缺漏
-
我觉得我好想只用了普及组的知识点
-
中间几个问题是什么鬼
-
我的代码能力似乎有很大进步
-
但思维能力有待很大提升
-
-
知识缺漏
-
不会数学推导,A 都推不出
-
没有熟练掌握算法的运用
-
策略缺失
好的:
-
我打了很多暴力
-
我发现正解写假后立刻写了对拍与暴力
不好的:
-
我没看 A 的部分分,根本没发现 \(n = 2\) 的提示性,直接写了假的暴力
-
写 D 前我没有验证做法正确性,导致浪费 1h+ 用于写假做法与对拍
-
B 我没有模拟操作,对操作的性质一无所知
改进方案
-
要多做比赛,比赛时算法不会给出 tag
-
上课要多想,比赛时的做法都得自己想
-
多对题目做总结
-
作业要线下先验证一遍
-
测极限数据
-
样例过了 \(\neq\) AC
-
07/20
上课内容
组合数学
-
下降幂 \(n^{\underline{m}}=A_n^m\)。
-
\(\sum_{i = 0}^m \binom{i}{n} =\binom{m+1}{n+1}\),这个东西好像是可以用杨辉三角证明。
-
\(\sum_{i = 0}^k \binom{n}{i} \binom{m}{k - i} = \binom{n + m}{k}\),范德蒙德卷积,可以用二项式定理证明。
-
\(\binom{n}{r} \binom{r}{k} = \binom{n}{k} \binom{n - k}{r - k}\),组合意义:选 \(r\) 个人,\(r\) 个人里选 \(k\) 个发奖。后面是先发奖,再选炮灰。
-
二项式定理。
-
二项式反演。
- 如果 \(g_n = \sum_{i = 0} ^ n \binom{n}{i} f_i\),则
\[f_n = \sum_{i = 0}^n (-1)^{n - i} \binom{n}{i} g_i \]可以带入啊算个半天,最后得出来了。
-
错排
-
加入,交换,\(n - 2\) 条限制
-
加入,交换,加上 \(i\) 不在 \(n\) 的限制,共 \(n - 1\) 条限制
-
加起来,\(D_n = (n - 1)(D_{n - 1} + D_{n - 2})\)。
-
或者还有 OI-Wiki 的理解方式。
-
还有一个式子:\(D_n = \lfloor \frac{n!}{e} \rfloor\)
-
线性代数初步
-
矩阵的一些运算
-
原来 \(\det\) 的定义是凑出来的 😃
-
注意矩阵乘法无交换律,但有结合律,就可以用线段树搞动态 DP 之类的东西,当然我不会
-
-
矩阵幂,线性递推
例题
-
\[2^{n - 3} \times \binom{m}{n-1} \times (n - 2) \]
式子做的时候重新推。
-
把老师当 boys - 把老师绑一块成一个 boy
-
推式子。
-
矩阵乘法水题
-
矩阵乘法改写再矩阵快速幂水题
-
类线性递推
- 添加一些常数或者什么项。
-
魔法值
就是以前秦老师讲过的 \(O(q n^3 \log a)\) 用矩阵乘法结合律优化为 \(O(q n^2 \log a)\) 的题,记得吧!
预处理 \(X^{2^k}\),从右到左乘一遍,\(O(n^3 \log a + q n^2 \log a)\)。预处理平衡复杂度。
07/20-2
怎么还上课。
Gauss Elimination
-
高斯消元
-
逆矩阵
-
行列式
-
一个图,每次可以选一条边,将端点的 \(0/1\) 状态取反。开始时全 \(0\),问能否全 \(1\)。
- 相邻异或和 \(=1\),解方程,
bitset
优化。
- 相邻异或和 \(=1\),解方程,
-
线性基
-
\(X_r \oplus X_b\) 是一定的,如果总体异或和的某一位是 \(1\),置零,因为不论哪边是 \(1/0\) 都没关系,对于 \(0\) 的位,线性基。
-
-
高斯消元与 DP
-
无限次操作、DP 非 DAG 的解方程做法。
-
07/21
模拟赛,\(100 / 100 + 100 / 100 + 20 / 100 + 0 / 100 = 220 / 400\), rank. 1,这把我开了。
07/22
-
好些的整数三分
while (r - l > 3) { int lmid = l + (r - l) / 3, rmid = r - (r - l) / 3; if (f(lmid) < f(rmid)) l = lmid; else r = rmid; } auto top = f(l); while (f(l + 1) > f(l)) l++;
例题
-
- \(w - mid\) 最小环,负环判断。
-
三个操作,知道总代价没用。
二分好像没相关的单调性,因此三分最终所有楼的高度 \(H\) 即可。
-
首先我们可以简单转化为求多少个 \((i, j)\) 满足 \(\max\{y_i \dots y_j\} - \min\{y_i \dots y_j\} = j - i\)
分治,递归上来时记录前后缀极值。合并出的贡献是 \(\max (suf_i, pre_j) - \min(suf_i, pre_j) = j - i\)。我们拆 \(\max \min\) 并分讨即可。一种情况直接做,另一种情况双指针。
07/23
模拟赛,寄大了,只有 rk.8。
总结
这把又打出了我 OI 里的一堆问题,当然不妨先写一点点优点:
- 通读了题目
哈哈,竟然想不出别的 💔
首先策略上自带极大的问题,我用大半场的时间死磕 B 题,导致 C 没认真看,最后 B 也没想出来。
其次,在思考方向方法上也有巨大问题——可怕的 dfs 式想题。在思考 B 一个多小时,搞出了好几个假做法后,任然没有一个可行的解时,我任然在考虑 B 如何用图论做(这是我想不出来的,最大团是 NPC)。如果我第一次假后就换成数论 + DP 版的思考方向应该能拿很多分。部分分也提示了这一点,但我没有醒悟过来。
最后,我不怎么会打暴力,如果是指数级的暴力是可以剪枝的,然而我没有。这就导致 B 题分数直接垫底。
其实这么看,上一场是很狗运的。我一遍想出了 AB 的做法,并且一遍写对,实在是遇到了喜欢的题。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下