2025-12-04 hetao1733837的刷题记录
然后字符串就是复习 Manacher,KMP,哈希,字典树,AC自动机,多学一下二分哈希,后缀自动机,后缀数组,也就差不多了。然后,树上的就是树剖,LCT,点分治,边分治,点分树,出不多就点满了,不知道会不会写题,但是先按这个学吧。考虑这题怎么写,由于经过树剖之后,链上的 dfs 序连续,所以,转化为单点修改,区间查第一个被修改的。行,2025年结束之前尽全力全部过一遍,多刷题,通过数冲750+吧
2025-12-04 hetao1733837的刷题记录
LG1495 【模板】中国剩余定理(CRT)/ 曹冲养猪
分析
很版,但是,我好像并不会 exgcd 求逆,我是**/(ㄒoㄒ)/~~
正解
#include <bits/stdc++.h>
#define int __int128
using namespace std;
const int N = 15;
int n, a[N], b[N];
int exgcd(int a, int b, int &x, int &y){
if (b == 0){
x = 1;
y = 0;
return a;
}
int k = exgcd(b, a % b, x, y);
int tmp = x;
x = y;
y = tmp - a / b * y;
return k;
}
int crt(int r[], int m[]){
int M = 1, ans = 0;
for (int i = 1; i <= n; i++)
M *= m[i];
for (int i = 1; i <= n; i++){
int c = M / m[i];
int x, y;
exgcd(c, m[i], x, y);
ans = (ans + (r[i] * c * x) % M) % M;
}
return (ans % M + M) % M;
}
int read(){
char c = getchar();
int x = 0, f = 1;
while (c < '0' || c > '9'){
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
x = x * 10 + (c - '0');
c = getchar();
}
return x * f;
}
void write(int x){
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
n = read();
for (int i = 1; i <= n; i++){
a[i] = read();
b[i] = read();
}
write(crt(b, a));
}
LG4777 【模板】扩展中国剩余定理(EXCRT)
原题链接:【模板】扩展中国剩余定理(EXCRT)
分析
此题不保证模数互质了。所以扩展了,像在废话。所以使用迭代法一层一层最终得到答案。
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005;
int n, a[N], m[N];
int mul(int a, int b, int mod){
int res = 0;
while (b){
if (b & 1)
res = (res + a) % mod;
a = (a + a) % mod;
b >>= 1;
}
return res;
}
int exgcd(int a, int b, int &x, int &y){
if (b == 0){
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int excrt(){
int x, y;
int M = m[1];
int ans = a[1] % M;
for (int i = 2; i <= n; i++){
int c = (a[i] - ans % m[i] + m[i]) % m[i];
int d = exgcd(M, m[i], x, y);
if (c % d != 0)
return -1;
x = mul(x, c / d, m[i] / d);
ans += x * M;
M = M / d * m[i];
ans = (ans % M + M) % M;
}
return ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> m[i] >> a[i];
}
cout << excrt();
return 0;
}
嘻嘻,回机房了,lz 能让我停文科吗?
学了树剖,先做点板子,然后考虑更强的思维。
感觉是时候把所有东西往满了点了,毕竟以我 NOIP 的分数,省选压力挺大的。
然后,树上的就是树剖,LCT,点分治,边分治,点分树,出不多就点满了,不知道会不会写题,但是先按这个学吧。
然后字符串就是复习 Manacher,KMP,哈希,字典树,AC自动机,多学一下二分哈希,后缀自动机,后缀数组,也就差不多了。
数据结构,平衡树,可持久化数据结构,也差不多没了。
数学,高中数学加紧学完,强化一下数数,这个很有必要,期望、概率论也是重点。
动态规划的话,我觉得数位DP,概率DP都可以提上日程,这个待定吧……主要是DP优化省选常考。
图论的话,就是Tarjan,这个以前接触过,但是不完全会,也要复习。
行,2025年结束之前尽全力全部过一遍,多刷题,通过数冲750+吧,每周打一下CF,差不多。
行,树剖,开始!
LG3384 【模板】重链剖分/树链剖分
原题链接:【模板】重链剖分/树链剖分
分析
主要还是懒标记下放,这个是极弱点,必须加强!
其他的没啥,主要记录一个 r k rk rk 数组,和 d f n dfn dfn 一起使用。
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005;
int n, m, r, mod, a[N];
int dfn[N], son[N], fa[N], de[N], sz[N], top[N]; //top表示重链顶
vector<int> e[N];
int ncnt;
int op, x, y, z;
int rk[N];
struct segtree{
int sum[N << 2], tag[N << 2];
void pushup(int p){
sum[p] = (sum[p << 1] + sum[p << 1 | 1]) % mod;
}
void down(int p, int l, int r){
if (!tag[p])
return ;
int mid = (l + r) >> 1;
tag[p << 1] = (tag[p << 1] + tag[p]) % mod;
sum[p << 1] = (sum[p << 1] + tag[p] * (mid - l + 1) % mod) % mod;
tag[p << 1 | 1] = (tag[p << 1 | 1] + tag[p]) % mod;
sum[p << 1 | 1] = (sum[p << 1 | 1] + tag[p] * (r - mid) % mod) % mod;
tag[p] = 0;
}
void build(int p, int l, int r){
if (l == r){
sum[p] = a[rk[l]] % mod;
return ;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
void modify(int p, int l, int r, int s, int t, int v){
if (s <= l && r <= t){
tag[p] = (tag[p] + v) % mod;
sum[p] = (sum[p] + v * (r - l + 1) % mod) % mod;
return ;
}
down(p, l, r);
int mid = (l + r) >> 1;
if (s <= mid)
modify(p << 1, l, mid, s, t, v);
if (t > mid)
modify(p << 1 | 1, mid + 1, r, s, t, v);
pushup(p);
}
int query(int p, int l, int r, int s, int t){
if (s <= l && r <= t){
return sum[p];
}
down(p, l, r);
int mid = (l + r) >> 1;
int ans = 0;
if (s <= mid)
ans = (ans + query(p << 1, l, mid, s, t)) % mod;
if (t > mid)
ans = (ans + query(p << 1 | 1, mid + 1, r, s, t)) % mod;
return ans;
}
}T;
void dfs1(int u, int f){
fa[u] = f;
sz[u] = 1;
de[u] = de[f] + 1;
for (auto v : e[u]){
if (v == f)
continue;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]])
son[u] = v; //更新重儿子
}
}
void dfs2(int u, int tp){
top[u] = tp;
dfn[u] = ++ncnt;
rk[dfn[u]] = u;
if (son[u])
dfs2(son[u], tp);
for (auto v : e[u]){
if (v == fa[u] || v == son[u])
continue;
dfs2(v, v);
}
}
void subadd(int u, int v){ //子树加
T.modify(1, 1, n, dfn[u], dfn[u] + sz[u] - 1, v);
}
int subquery(int u){ //子树查
return T.query(1, 1, n, dfn[u], dfn[u] + sz[u] - 1) % mod;
}
void pathadd(int u, int v, int val){ //路径加
while (top[u] != top[v]){
if (de[top[u]] < de[top[v]])
swap(u, v);
T.modify(1, 1, n, dfn[top[u]], dfn[u], val);
u = fa[top[u]];
}
if (de[u] > de[v])
swap(u, v);
T.modify(1, 1, n, dfn[u], dfn[v], val);
}
int pathquery(int u, int v){ //路径查
int res = 0;
while (top[u] != top[v]){
if (de[top[u]] < de[top[v]])
swap(u, v);
res = (res + T.query(1, 1, n, dfn[top[u]], dfn[u])) % mod;
u = fa[top[u]];
}
if (de[u] > de[v])
swap(u, v);
res = (res + T.query(1, 1, n, dfn[u], dfn[v])) % mod;
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> r >> mod;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
for (int i = 1; i < n; i++){
cin >> x >> y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs1(r, 0);
dfs2(r, r);
T.build(1, 1, n);
for (int i = 1; i <= m; i++){
cin >> op;
if (op == 1){
cin >> x >> y >> z;
pathadd(x, y, z);
}
if (op == 2){
cin >> x >> y;
cout << pathquery(x, y) << '\n';
}
if (op == 3){
cin >> x >> z;
subadd(x, z);
}
if (op == 4){
cin >> x;
cout << subquery(x) << '\n';
}
}
return 0;
}
LG3038 [USACO11DEC] Grass Planting G
原题链接:[USACO11DEC] Grass Planting G
分析
何意味?我稍考一下,虽然很板吧……
跳到最后,只剩下一条边,所以,dfs 序上要做个 +1,否则是 WA 的。
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005;
int n, m, u, v;
char op;
int sz[N], fa[N], top[N], dfn[N], de[N], ncnt, son[N];
int rk[N];
vector<int> e[N];
void dfs1(int u, int f){
sz[u] = 1;
fa[u] = f;
for (auto v : e[u]){
if (v == f) continue;
de[v] = de[u] + 1;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]])
son[u] = v;
}
}
void dfs2(int u, int tp){
top[u] = tp;
dfn[u] = ++ncnt;
rk[ncnt] = u;
if (son[u])
dfs2(son[u], tp);
for (auto v : e[u]){
if (v == fa[u] || v == son[u])
continue;
dfs2(v, v);
}
}
struct segtree{
int sum[N * 4], tag[N * 4];
void pushup(int p){
sum[p] = sum[p << 1] + sum[p << 1 | 1];
}
void build(int p, int l, int r){
if (l == r){
sum[p] = tag[p] = 0;
return ;
}
int mid = (l + r) / 2;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
pushup(p);
}
void down(int p, int l, int r){
if (tag[p]){
int mid = (l + r) / 2;
tag[p << 1] += tag[p];
tag[p << 1 | 1] += tag[p];
sum[p << 1] += tag[p] * (mid - l + 1);
sum[p << 1 | 1] += tag[p] * (r - mid);
tag[p] = 0;
}
}
void modify(int p, int l, int r, int s, int t, int v){
if (l >= s && r <= t){
tag[p] += v;
sum[p] += v * (r - l + 1);
return ;
}
down(p, l, r);
int mid = (l + r) / 2;
if (s <= mid)
modify(p * 2, l, mid, s, t, v);
if (t > mid)
modify(p * 2 + 1, mid + 1, r, s, t, v);
pushup(p);
}
int query(int p, int l, int r, int s, int t){
if (l >= s && r <= t)
return sum[p];
down(p, l, r);
int mid = (l + r) / 2;
int ans = 0;
if (s <= mid)
ans += query(p * 2, l, mid, s, t);
if (t > mid)
ans += query(p * 2 + 1, mid + 1, r, s, t);
return ans;
}
}T;
void add(int u, int v){
while (top[u] != top[v]){
if (de[top[u]] < de[top[v]])
swap(u, v);
T.modify(1, 1, n, dfn[top[u]], dfn[u], 1);
u = fa[top[u]];
}
if (de[u] < de[v])
swap(u, v);
if (u != v)
T.modify(1, 1, n, dfn[v] + 1, dfn[u], 1); //只有中间一条边,需要注意
}
int ask(int u, int v){
int ans = 0;
while (top[u] != top[v]){
if (de[top[u]] < de[top[v]])
swap(u, v);
ans += T.query(1, 1, n, dfn[top[u]], dfn[u]);
u = fa[top[u]];
}
if (de[u] < de[v])
swap(u, v);
if (u != v)
ans += T.query(1, 1, n, dfn[v] + 1, dfn[u]); //只有中间一条边,需要注意
return ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i < n; i++){
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1, 0);
dfs2(1, 1);
T.build(1, 1, n);
for (int i = 1; i <= m; i++){
cin >> op;
if (op == 'P'){
cin >> u >> v;
add(u, v);
}
if (op == 'Q'){
cin >> u >> v;
cout << ask(u, v) << '\n';
}
}
}
LG4092 [HEOI2016/TJOI2016] 树
分析
这是树剖Σ(っ °Д °;)っ
我学的树剖是假的吗/(ㄒoㄒ)/~~
首先,我们要明确如何使用树链剖分求 LCA,发现是跳 top,那很🍬。
考虑这题怎么写,由于经过树剖之后,链上的 dfs 序连续,所以,转化为单点修改,区间查第一个被修改的。
那离线做个并查集,反过来做,同一个最近被修改的为一个连通块,修改变为删除,破除连通性即可。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, q, u, v;
struct ask{
char op;
int id, res;
}ans[N];
vector<int> e[N];
int fa[N]; //并查集
int father[N]; //父亲
int tag[N];
void dfs(int u, int f){
father[u] = f;
if (tag[u])
fa[u] = u;
else
fa[u] = f;
for (auto v : e[u]){
if (v == f)
continue;
dfs(v, u);
}
}
int find(int x){
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> q;
for (int i = 1; i < n; i++){
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
tag[1] = 1;
for (int i = 1; i <= q; i++){
cin >> ans[i].op >> ans[i].id;
if (ans[i].op == 'C'){
tag[ans[i].id]++;
}
}
dfs(1, 0);
father[1] = 1;
fa[1] = 1;
for (int i = q; i >= 1; i--){
if (ans[i].op == 'C'){
tag[ans[i].id]--;
if (!tag[ans[i].id])
fa[ans[i].id] = father[ans[i].id];
}
else{
ans[i].res = find(ans[i].id);
}
}
for (int i = 1; i <= q; i++){
if (ans[i].op == 'Q')
cout << ans[i].res << '\n';
}
}
更多推荐


所有评论(0)