2025-12-02~03 hetao1733837的刷题记录
12-02
LG2391 白雪皑皑
原题链接:白雪皑皑
分析
呃,一眼线段树,但是我好像不会,就是说是区间赋值没毛病,真的直接赋值吗?那很好了。
oh,难点在于多次修改,而且修改次数是 1 × 1 0 7 1\times 10^7 1×107 级别的, l o g log log 根本跑步下。那么就要预处理出类似于线段覆盖的东西,那岂不是思维题然后 不用数据结构了?这么神秘?难道是同余方程求解?这么牛?那不就成🍬题了?
我是🍬......
区间多次覆盖,想到了并查集,然后用并查集维护,倒序枚举最后一次覆盖是什么颜色,做完了。
我还是觉得解同余方程有前途。
同时,我看的那篇题解给出了另一道题,有空了再写,要是哪位看到博客,可以在评论区提醒我回来写!/bx
设 f a i fa_i fai 表示 i i i 位置后面第一个没有被染色的,中间统一染成一个颜色,没了。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000005;
int n, m, p, q;
int col[N], fa[N];
int find(int x){
return x == fa[x] ? fa[x] : fa[x] = find(fa[x]);
}
void merge(int x, int y){
fa[x] = y;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> p >> q;
for (int i = 1; i <= n; i++){
fa[i] = i;
}
for (int i = m; i >= 1; i--){
int l = (i * p + q) % n + 1;
int r = (i * q + p) % n + 1;
if (l > r){
swap(l, r);
}
for (int j = r; j >= l; ){
int faj = find(j);
if (faj == j){
col[j] = i;
merge(j, find(j - 1));
}
j = fa[j];
}
}
for (int i = 1; i <= n; i++)
cout << col[i] << '\n';
}
然后就是学点新东西吧,线段树上二分,线段树分治,线段树分裂,线段树合并,李超线段树......🤮
可持久化也是必备技能吧。
12-03
LG13979 数列分块入门 4
原题链接:数列分块入门 4
分析
呃······我不清楚是不是会了。思想很顺理成章,我也知道代码为啥这么写,但是还是不会自己写。
我理解错了,块的个数不等于块的长度! 然后样例里长度为2,个数也为2,炸了。
还有,别忘了 懒标记下放!
何意味,调不出来/(ㄒoㄒ)/~~
正解
何意味,我代码不是和正解一样吗?改个变量名就WA?哦,真的要开到500吗?
cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 300005, M = 555;
int a[N], tag[M], sum[M];
int n, op, l, r, c;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
int len = sqrt(n);
for (int i = 1; i <= n; i++){
cin >> a[i];
sum[(i - 1) / len + 1] = sum[(i - 1) / len + 1] + a[i];
}
int t = n;
while (t--){
cin >> op >> l >> r >> c;
int nl = (l - 1) / len + 1, nr = (r - 1) / len + 1;
if (op == 0){
if (nl == nr){
for (int i = l; i <= r; i++){
a[i] += c;
sum[nl] = sum[nl] + c;
}
}
else{
for (int i = l; i <= nl * len; i++){
a[i] += c;
sum[nl] = sum[nl] + c;
}
for (int i = nl + 1; i < nr; i++){
tag[i] += c;
sum[i] += len * c;
}
for (int i = (nr - 1) * len + 1; i <= r; i++){
a[i] += c;
sum[nr] += c;
}
}
}
if (op == 1){
c++;
int ans = 0;
if(nl == nr){
for (int i = l; i <= r; i++){
ans += a[i] + tag[nl];
}
}
else{
for (int i = l; i <= nl * len; i++){
ans += a[i] + tag[nl];
}
for (int i = nl + 1; i < nr; i++){
ans += sum[i];
}
for (int i = (nr - 1) * len + 1; i <= r; i++){
ans += a[i] + tag[nr];
}
}
cout << (ans % c + c) % c << "\n";
}
}
}
LG2357 守墓人
原题链接:守墓人
分析
刷题解看到了这篇,感觉题目名很浪漫,决定写一写。
看了难度,应该是树状数组板子吧......没过/(ㄒoㄒ)/~~
呃,开两个,分别记录单点修改的值和区间和,没了,整体担心复杂度可以单开一个 t a g tag tag,优化挺大的。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005;
int n, f, a[N], c1[N], c2[N];
int op, l, r, k;
int tag;
void add(int x, int v){
for (int i = x; i <= n; i += i & (-i)){
c1[i] += v;
c2[i] += x * v;
}
}
int query(int x){
int ans = 0;
for (int i = x; i; i -= i & (-i)){
ans += (x + 1) * c1[i] - c2[i];
}
return ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> f;
for (int i = 1; i <= n; i++){
cin >> a[i];
add(i, a[i] - a[i - 1]);
}
for (int cs = 1; cs <= f; cs++){
cin >> op;
if (op == 1){
cin >> l >> r >> k;
add(l, k);
add(r + 1, -k);
}
if (op == 2){
cin >> k;
tag += k;
}
if (op == 3){
cin >> k;
tag -= k;
}
if (op == 4){
cin >> l >> r;
int ans = query(r) - query(l - 1);
if (l == 1)
ans += tag;
cout << ans << '\n';
}
if (op == 5){
cout << query(1) + tag << '\n';
}
}
}
LG10463 Interval GCD
原题链接:Interval GCD
分析
区间 gcd \gcd gcd 能线段树吗?我咋不太信啊......显然,我们见多识广,在核桃国庆的时候,出现了用 ST 表统计区间 gcd \gcd gcd 的操作,lrh 告诉我,ST 表能干的事,线段树也能干!可合并就是可用线段树维护,懂了!
何意味?我居然不会down!呃,似乎不变(辗转相减发得出)。
我是**,有一个性质: gcd ( a 1 , a 2 , . . . , a n ) = gcd ( a 1 , a 1 − a 2 , a 3 − a 2 , . . . , a n − a n − 1 ) \gcd(a_1, a_2,...,a_n)=\gcd(a_1, a_1-a_2,a_3-a_2,...,a_n-a_{n-1}) gcd(a1,a2,...,an)=gcd(a1,a1−a2,a3−a2,...,an−an−1)
那就多个差分数组的事了!
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500005;
int n, m, a[N];
char op;
int l, r, d;
struct segtree{
int sum[N << 2], gcd[N << 2];
void pushup(int p) {
sum[p] = sum[p << 1] + sum[p << 1 | 1];
gcd[p] = __gcd(gcd[p << 1], gcd[p << 1 | 1]);
}
void build(int p, int l, int r){
if (l == r) {
sum[p] = gcd[p] = a[l] - a[l - 1];
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 pos, int v){
if (l == r) {
sum[p] += v;
gcd[p] += v;
return ;
}
int mid = (l + r) >> 1;
if (pos <= mid)
modify(p << 1, l, mid, pos, v);
else
modify(p << 1 | 1, mid + 1, r, pos, v);
pushup(p);
}
int query_sum(int p, int l, int r, int s, int t){
if (s <= l && r <= t){
return sum[p];
}
int mid = (l + r) >> 1;
int ans = 0;
if (s <= mid)
ans += query_sum(p << 1, l, mid, s, t);
if (t > mid)
ans += query_sum(p << 1 | 1, mid + 1, r, s, t);
return ans;
}
int query_gcd(int p, int l, int r, int s, int t){
if (s <= l && r <= t){
return gcd[p];
}
int mid = (l + r) >> 1;
int ans = 0;
if (s <= mid)
ans = __gcd(ans, query_gcd(p << 1, l, mid, s, t));
if (t > mid)
ans = __gcd(ans, query_gcd(p << 1 | 1, mid + 1, r, s, t));
return ans;
}
} T;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
T.build(1, 1, n);
for (int cs = 1; cs <= m; cs++) {
cin >> op >> l >> r;
if (op == 'C'){
cin >> d;
T.modify(1, 1, n, l, d);
if (r + 1 <= n)
T.modify(1, 1, n, r + 1, -d);
}
if (op == 'Q'){
int g = T.query_sum(1, 1, n, 1, l);
if (l + 1 <= r)
g = __gcd(g, T.query_gcd(1, 1, n, l + 1, r));
cout << abs(g) << '\n';
}
}
return 0;
}
LG3293 [SCOI2016] 美味
原题链接:[SCOI2016] 美味
分析
所以,紫题终究还是切不了吗/(ㄒoㄒ)/~~
我认为应该开3个懒标记,分别记录区间异或值,区间和,还有......咋忘了?呃,按位取反吗?更恐怖了。
这就是初步思路,然后,我会区间异或,区间最值,区间加法,然后揉在一起?有点恐怖了。
行,看一下题解,写完去学整体二分,这才是对的,我还要复习一些神秘的东西。
我感觉数数是我需要严肃加强的。哎,真是千疮百孔的基础啊!
按位处理,然后那主席树维护一下,没了。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500005;
int n, m;
int b, x, l, r;
int a[N], ncnt, tr[N << 5], sum[N << 5];
int ls[N << 5], rs[N << 5];
void modify(int &p, int hp, int l, int r, int s){
if (s < l || s > r)
return ;
p = ++ncnt;
ls[p] = ls[hp];
rs[p] = rs[hp];
sum[p] = sum[hp] + 1;
if (l == r)
return ;
int mid = (l + r) >> 1;
if (s <= mid)
modify(ls[p], ls[hp], l, mid, s);
else
modify(rs[p], rs[hp], mid + 1, r, s);
}
int query(int p, int hp, int l, int r, int s, int t){
if (t < l || s > r)
return 0;
int cnt = sum[hp] - sum[p];
if (cnt == 0)
return 0;
if (s <= l && r <= t)
return cnt;
int mid = (l + r) >> 1;
return query(ls[p], ls[hp], l, mid, s, t) + query(rs[p], rs[hp], mid + 1, r, s, t);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
int mx = 0;
for (int i = 1; i <= n; i++){
cin >> a[i];
mx = max(mx, a[i]);
}
for (int i = 1; i <= n; i++){
modify(tr[i], tr[i - 1], 0, mx, a[i]);
}
for (int cs = 1; cs <= m; cs++){
cin >> b >> x >> l >> r;
int ans = 0;
for (int i = 18; i >= 0; i--){ //贪心寻找每个位置上的异或最大值
if (b & (1 << i)){
if (!query(tr[l - 1], tr[r], 0, mx, ans - x, ans - x + (1 << i) - 1))
ans += (1 << i);
}
else{
if (query(tr[l - 1], tr[r], 0, mx, ans - x + (1 << i), ans - x + (1 << (i + 1)) - 1))
ans += (1 << i);
}
}
cout << (ans ^ b) << '\n';
}
return 0;
}
叹口气......虽然进了省选,但是还差不少,努力进队吧,但是很难,省选里要多至少半道题,努力,加油(ง •_•)ง