2025-12-25~26 hetao1733837的刷题记录
12-25
LG9447 [ICPC 2021 WF] Spider Walk
原题链接:[ICPC 2021 WF] Spider Walk
分析
今天我一定要在洛谷上过一道紫!感觉是 + ∞ +\infty +∞ 天前 a o a o aoao aoao 推给我的,记得当时他还是半停课,我还是蒟蒻;现在他去冲省队了,我还是蒟蒻/ll
可能不是好题,算普通题,但是,很有意思。
我们设 d i , j d_{i,j} di,j 表示环上 i i i 到 j j j 的距离, f i f_i fi 表示从 s s s 到达 i i i 的最少搭桥数,显然起点设在 s s s 的无限远位置比较合适。经过我感性理解, ∣ f i − f j ∣ ≤ d i , j |f_{i}-f_{j}|\le d_{i,j} ∣fi−fj∣≤di,j。
初始 f i = d i , s f_i=d_{i,s} fi=di,s,从外向内加蛛丝,加蛛丝的两条射线会影响周围的一些射线之间的距离,则变成了区间减和区间最小值,上线段树即可。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 200005, M = 500005, inf = 0x3f3f3f3f;
int n, m, S;
pair<int, int> a[M];
struct segtree{
int tag1[N << 2], tag2[N << 2];
void down(int p, int l, int r){
int mid = (l + r) >> 1;
if (tag1[p] != inf){
tag1[p << 1] = min(tag1[p << 1], tag1[p]);
tag1[p << 1 | 1] = min(tag1[p << 1 | 1], tag1[p] + mid - l + 1);
tag1[p] = inf;
}
if (tag2[p] != inf){
tag2[p << 1] = min(tag2[p << 1], tag2[p]);
tag2[p << 1 | 1] = min(tag2[p << 1 | 1], tag2[p] - mid + l - 1);
tag2[p] = inf;
}
}
void build(int p, int l, int r){
tag1[p] = tag2[p] = inf;
if (l == r){
if (l == S){
tag1[p] = tag2[p] = 0;
}
else{
tag1[p] = tag2[p] = inf;
}
return ;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
}
void upd1(int p, int l, int r, int s, int t, int &v){
if (l > t || r < s)
return;
if (s <= l && r <= t){
tag1[p] = min(tag1[p], v);
v += (r - l + 1);
return ;
}
int mid = (l + r) >> 1;
down(p, l, r);
if (s <= mid)
upd1(p << 1, l, mid, s, t, v);
if (t > mid)
upd1(p << 1 | 1, mid + 1, r, s, t, v);
}
void upd2(int p, int l, int r, int s, int t, int &v){
if (l > t || r < s)
return;
if (s <= l && r <= t){
tag2[p] = min(tag2[p], v);
v -= (r - l + 1);
return ;
}
int mid = (l + r) >> 1;
down(p, l, r);
if (s <= mid)
upd2(p << 1, l, mid, s, t, v);
if (t > mid)
upd2(p << 1 | 1, mid + 1, r, s, t, v);
}
void update1(int p, int l, int r, int s, int v){
if (l == r){
tag1[p] = min(tag1[p], v);
tag2[p] = min(tag2[p], v);
return ;
}
int mid = (l + r) >> 1;
down(p, l, r);
if (s <= mid)
update1(p << 1, l, mid, s, v);
else
update1(p << 1 | 1, mid + 1, r, s, v);
}
void update2(int p, int l, int r, int s, int v) {
if (l == r){
tag1[p] = tag2[p] = v;
return ;
}
int mid = (l + r) >> 1;
down(p, l, r);
if (s <= mid)
update2(p << 1, l, mid, s, v);
else
update2(p << 1 | 1, mid + 1, r, s, v);
}
int query(int p, int l, int r, int s){
if (l == r)
return min(tag1[p], tag2[p]);
int mid = (l + r) >> 1;
down(p, l, r);
if (s <= mid)
return query(p << 1, l, mid, s);
else
return query(p << 1 | 1, mid + 1, r, s);
}
void write(int p, int l, int r) {
if (l == r){
cout << min(tag1[p], tag2[p]) << '\n';
return ;
}
int mid = (l + r) >> 1;
down(p, l, r);
write(p << 1, l, mid);
write(p << 1 | 1, mid + 1, r);
}
}T;
void change(int x){
T.update1(1, 1, n, x, min(T.query(1, 1, n, x % n + 1), T.query(1, 1, n, x > 1 ? x - 1 : n)) + 1);
}
void spread(int x) {
int g, f = T.query(1, 1, n, x);
T.upd1(1, 1, n, 1, x - 1, g = f + n - x + 1);
T.upd1(1, 1, n, x + 1, n, g = f + 1);
T.upd2(1, 1, n, 1, x - 1, g = f + x - 1);
T.upd2(1, 1, n, x + 1, n, g = f + n - 1);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> S;
for (int i = 1; i <= m; i++){
cin >> a[i].first >> a[i].second;
}
sort(a + 1, a + m + 1, greater<pair<int, int> >());
T.build(1, 1, n); spread(S);
for (int i = 1; i <= m; i++){
int x = a[i].second;
int y = x % n + 1;
int fx = T.query(1, 1, n, x), fy = T.query(1, 1, n, y);
T.update2(1, 1, n, y, fx);
T.update2(1, 1, n, x, fy);
change(x);
change(y);
spread(x);
spread(y);
}
T.write(1, 1, n);
}
这电脑抽什么风?我的刷题笔记和昨天自组模拟赛的总结全没了/ll
CF1748E Yet Another Array Counting Problem
原题链接1:E. Yet Another Array Counting Problem
原题链接2:CF1748E Yet Another Array Counting Problem
啊,我的思路!我的正解!全没了!
正确思路
我追忆 一下啊......

哦,有个关键性质就是说这个所谓的"最左端最大值位置"是支持合并的......呃,好像一句话概括完了。
那么,枚举断点,设 d p k , j dp_{k,j} dpk,j 表示 b k ≤ j b_k\le j bk≤j 时,区间 [ L , R ] [L,R] [L,R]的方案数。
转移则显然
d p k , j = d p k , j − 1 + d p k [ L ] , j − 1 × d p k [ R ] , j dp_{k,j}=dp_{k,j-1}+dp_{k[L], j-1}\times dp_{k[R],j} dpk,j=dpk,j−1+dpk[L],j−1×dpk[R],j
我觉得是这样吧......欸,我好像给花花有截屏!
总感觉少点什么呢......
正解
cpp
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
const int N = 200005;
int t;
int n, m, a[N];
vector<int> dp[N];
struct segtree{
int pos[N << 2];
void pushup(int p){
if (a[pos[p << 1]] < a[pos[p << 1 | 1]])
pos[p] = pos[p << 1 | 1];
else
pos[p] = pos[p << 1];
}
void build(int p, int l, int r){
if (l == r){
pos[p] = l;
return ;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
int query(int p, int l, int r, int s, int t){
if (s <= l && r <= t){
return pos[p];
}
int mid = (l + r) >> 1;
if (s > mid)
return query(p << 1 | 1, mid + 1, r, s, t);
else if (t <= mid)
return query(p << 1, l, mid, s, t);
int res1 = query(p << 1, l, mid, s, t);
int res2 = query(p << 1 | 1, mid + 1, r, s, t);
if (a[res1] < a[res2])
return res2;
else
return res1;
}
}T;
int dfs(int l, int r){
if (l > r)
return -1;
if (l == r){
for (int j = 1; j <= m; j++)
dp[l][j] = j;
return l;
}
int pos = T.query(1, 1, n, l, r);
int L = dfs(l, pos - 1);
int R = dfs(pos + 1, r);
if (L == -1){
for (int j = 1; j <= m; j++){
dp[pos][j] = (dp[R][j] + dp[pos][j - 1]) % mod;
}
}
else if (R == -1){
for (int j = 1; j <= m; j++){
dp[pos][j] = (dp[L][j - 1] + dp[pos][j - 1]) % mod;
}
}
else{
for (int j = 1; j <= m; j++){
dp[pos][j] = (dp[pos][j - 1] + dp[L][j - 1] * dp[R][j]) % mod;
}
}
return pos;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--){
cin >> n >> m;
for (int i = 1; i <= n; i++){
cin >> a[i];
dp[i].clear();
dp[i].resize(m + 5);
}
T.build(1, 1, n);
int k = dfs(1, n);
cout << dp[k][m] << '\n';
}
}
LG6829 [IOI 2020] 植物比较
原题链接:[IOI 2020] 植物比较
其实,个人觉得去LOJ交更能模拟那个不同的头文件。
正确思路
这个是个超强构造!构造原理就是对于一个位置,其前 k − 1 k-1 k−1 给位置都已经选了或都是 0 0 0,然后这个位置是 0 0 0,将其赋值为当前序列最大值。
这样就可以构造出来了,线段树+set 优化到 O ( n l o g n ) O(nlogn) O(nlogn)。
然后,对于 2 × k > n 2\times k > n 2×k>n 的,显然有唯一构造,获得很多部分分!
然后对每个位置记录 p r e pre pre 和 n x t nxt nxt 表示向左/右跳 k k k 个位置比他小的数的最大的位置,套一个倍增。
对于询问 ( x , y ) (x,y) (x,y),假定 h x > h y h_x>h_y hx>hy,然后从 x x x 出发开始跳,如果跳到 y y y 的过程中每一个位置都 > h y >h_y >hy,那就可行了。
反之,则假定 h y > h x h_y>h_x hy>hx,同理。
若都无法得到答案,输出 0 即可。
正解
cpp
#include <bits/stdc++.h>
// #include "plants.h"
#define LL long long
using namespace std;
const int N = 200005;
const int inf = 0x7f7f7f7f;
int n, k, R[N];
struct segtree2{
int cur[N], node[N], mx[N << 2];
int maxn(int x, int y){
if (cur[x] > cur[y])
return x;
else
return y;
}
void pushup(int p){
mx[p] = maxn(mx[p << 1], mx[p << 1 | 1]);
}
void build(int p, int l, int r){
if (l == r){
node[l] = p;
mx[p] = l;
return ;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
void init1(){
memset(cur, 0, (n + 1) << 2);
build(1, 1, n);
}
void modify(int p, int v){
if (p < 1 || p > n){
return ;
}
static int tmp;
mx[node[p]] = p;
tmp = node[p] >> 1;
cur[p] = v;
while (tmp){
pushup(tmp);
tmp >>= 1;
}
}
int got(){
static int res;
res = mx[1];
if (cur[res] >= k){
modify(res, 0);
return res;
}
else{
return 0;
}
}
int ll, rr, ans;
void init2(){
memset(cur, 0, (n + 1) << 2);
memset(mx, 0, (n * 4 + 1) << 2);
cur[0] = -1;
}
void query1(int p, int l, int r){
if (ll <= l && r <= rr){
ans = maxn(ans, mx[p]);
return ;
}
int mid = (l + r) >> 1;
if (ll <= mid){
query1(p << 1, l, mid);
}
if (rr > mid){
query1(p << 1 | 1, mid + 1, r);
}
}
int query2(int l, int r){
ans = 0;
ll = max(1, l);
rr = min(n, r);
if (ll <= rr)
query1(1, 1, n);
return ans;
}
}T2;
set<int> S;
bool buc[N];
typedef set<int>::iterator IT;
void recalc(int x){
static IT it;
if (buc[x]){
it = S.find(x);
if (it == S.begin()){
it = S.end();
it--;
if (*it == x){
T2.modify(x, n);
}
else{
T2.modify(x, x + n - *it);
}
}
else{
--it;
T2.modify(x, x - *it);
}
}
else{
T2.modify(x, 0);
}
}
void Ins(int x){
static IT it;
buc[x] = 1;
it = S.insert(x).first;
it++;
if (it == S.end())
it = S.begin();
recalc(x);
recalc(*it);
}
void Del(int x){
static IT it;
static int v;
buc[x] = 0;
it = S.find(x);
it++;
if (it == S.end()){
it = S.begin();
}
v = *it;
S.erase(S.find(x));
recalc(v);
recalc(x);
}
struct segtree1{
int tag[N << 2], mn[N << 2];
void pushup(int p){
mn[p] = min(mn[p << 1], mn[p << 1 | 1]);
}
void maketag(int p, int v){
tag[p] += v;
mn[p] += v;
}
void down(int p){
if (tag[p]){
maketag(p << 1, tag[p]);
maketag(p << 1 | 1, tag[p]);
tag[p] = 0;
}
}
void build(int p, int l, int r){
if (l == r){
mn[p] = R[l];
return ;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
int ll, rr;
void update(int p, int l, int r){
if (ll <= l && r <= rr){
maketag(p, -1);
return ;
}
down(p);
int mid = (l + r) >> 1;
if (ll <= mid)
update(p << 1, l, mid);
if (rr > mid)
update(p << 1 | 1, mid + 1, r);
pushup(p);
}
void Work(int p, int l, int r){
if (mn[p] > 0)
return ;
if (l == r){
mn[p] = inf;
Ins(l);
return ;
}
down(p);
int mid = (l + r) >> 1;
Work(p << 1, l, mid);
Work(p << 1 | 1, mid + 1, r);
pushup(p);
}
void init(){
build(1, 1, n);
}
void work(){
Work(1, 1, n);
}
void upd(int l, int r){
ll = l;
rr = r;
if (ll <= rr){
update(1, 1, n);
}
}
}T1;
int a[N], b[N], la[N][20], ne[N][20], ld[N][20], nd[N][20], L;
void Main(){
T2.init1();
T1.init();
T1.work();
int i, j, x, p, now = n;
while (now){
x = T2.got();
a[x] = now;
now--;
Del(x);
if (x >= k){
T1.upd(x - k + 1, x);
}
else{
T1.upd(1, x);
T1.upd(n - (k - x) + 1, n);
}
T1.work();
}
for (i = 1; i <= n; ++i){
b[a[i]] = i;
}
T2.init2();
for (i = 1; i <= n; ++i){
p = b[i];
la[p][0] = T2.query2(p - k + 1, p - 1);
if (la[p][0]){
ld[p][0] = p - la[p][0];
}
ne[p][0] = T2.query2(p + 1, p + k - 1);
if (ne[p][0]){
nd[p][0] = ne[p][0] - p;
}
if (p - k + 1 < 1){
x = T2.query2(p - k + 1 + n, n);
if (a[x] > a[la[p][0]]){
la[p][0] = x;
ld[p][0] = p + n - x;
}
}
if (p + k - 1 > n){
x = T2.query2(1, p + k - 1 - n);
if (a[x] > a[ne[p][0]]){
ne[p][0] = x;
nd[p][0] = x + n - p;
}
}
T2.modify(p, i);
}
L = 1;
while ((1 << L) < n)
L++;
for (j = 1; j <= L; j++){
for (i = 1; i <= n; i++){
if ((p = la[i][j - 1])){
la[i][j] = la[p][j - 1];
ld[i][j] = min(n, ld[i][j - 1] + ld[p][j - 1]);
}
p = ne[i][j - 1];
if (p){
ne[i][j] = ne[p][j - 1];
nd[i][j] = min(n, nd[i][j - 1] + nd[p][j - 1]);
}
}
}
}
void init(int K, vector<int> R_vec){
k = K;
n = R_vec.size();
for (int i = 1; i <= n; i++){
::R[i] = R_vec[i - 1];
}
Main();
}
bool checkbig(int x, int y){
int i, xx = x, delta = y - x, s;
for (s = 0, i = L; i >= 0; i--){
if (a[ne[x][i]] >= a[y]){
s += nd[x][i];
x = ne[x][i];
}
}
if (s >= delta)
return 1;
for (s = 0, x = xx, i = L; i >= 0; i--){
if (a[la[x][i]] >= a[y]){
s += ld[x][i];
x = la[x][i];
}
}
if (s >= n - delta)
return 1;
return 0;
}
bool checksmall(int x, int y){
int i, yy = y, delta = y - x, s;
for (s = 0, i = L; i >= 0; i--){
if (a[la[y][i]] >= a[x]){
s += ld[y][i];
y = la[y][i];
}
}
if (s >= delta)
return 1;
for (s = 0, y = yy, i = L; i >= 0; i--){
if (a[ne[y][i]] >= a[x]){
s += nd[y][i];
y = ne[y][i];
}
}
if (s >= n - delta)
return 1;
return 0;
}
int compare_plants(int x, int y){
x++;
y++;
if (a[x] > a[y])
return checkbig(x, y) ? 1 : 0;
return checksmall(x, y) ? -1 : 0;
}