2026-09-13 hetao1733837 的刷题记录

LGP14989 传送

原题链接:传送

分析

别急,我忘了第一个大于的怎么求了😭

感觉这会是一个很典的 Trick


跳到一个公共的点 → \rightarrow → LCA

第一个大于的,显然是个 单调栈

我们贪心地发现,每次往左右两边更小的值跳就行了,因为这个会把中间的东西都集中起来,如果跳更大的很容易跳不动了。然后答案要求尽可能多,所以,跳较小的。

那么,可以建树了!

然后直接做就行了!

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long 
using namespace std;
const int N = 500005;
int n, q, p[N];
stack<int> stk;
int lp[N], rp[N];
int f[N][25], de[N]; 
int head[N << 1], tot;
struct edge{
    int u, to, nxt;
}e[N << 1];
void add(int u, int v){
    e[++tot].u = u;
    e[tot].nxt = head[u];
    e[tot].to = v;
    head[u] = tot;
}
void dfs(int u, int fa){
    f[u][0] = fa;
    de[u] = de[fa] + 1;
    for (int i = head[u]; i; i = e[i].nxt){
        int v = e[i].to;
        if (v == fa)
            continue;
        dfs(v, u);
    }
}
void init(){
    for (int j = 1; j <= 20; j++){
        for (int u = 1; u <= n; u++){
            f[u][j] = f[f[u][j - 1]][j - 1];
        }
    }
}
int LCA(int x, int y){
    if (de[x] < de[y])
        swap(x, y);
    int delta = de[x] - de[y];
    for (int i = 20; i >= 0; i--){
        if (delta & (1 << i)){
            x = f[x][i];
        }
    }
    if (x == y)
        return x;
    for (int i = 20; i >= 0; i--){
        if (f[x][i] != f[y][i]){
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> q;
    for (int i = 1; i <= n; i++){
        cin >> p[i];
    }
    for (int i = 1; i <= n; i++){
        while (!stk.empty() && p[stk.top()] <= p[i]){
            stk.pop();
        }
        if (stk.empty()){
            lp[i] = 0x3f3f3f3f3f3f3f3f;
        } 
        else{
            lp[i] = p[stk.top()];
        }
        stk.push(i);
    }
    while (!stk.empty()) 
        stk.pop();
    for (int i = n; i >= 1; i--){
        while (!stk.empty() && p[stk.top()] <= p[i]){
            stk.pop();
        }
        if (stk.empty()){
            rp[i] = 0x3f3f3f3f3f3f3f3f;
        } 
        else{
            rp[i] = p[stk.top()];
        }
        stk.push(i);
    }
    for (int i = 1; i <= n; i++){
        int lv = lp[i], rv = rp[i];
        int minn = min(lv, rv);
        if (minn == 0x3f3f3f3f3f3f3f3f) 
            continue;
        add(p[i], minn);
        add(minn, p[i]);
    }
    dfs(n, 0);
    init();
    for (int cs = 1, k, id; cs <= q; cs++){
        cin >> k;
        cin >> id;
        id = p[id];
        for (int i = 2, a; i <= k; i++){
            cin >> a;
            id = LCA(id, p[a]);
        }
        cout << de[id] << '\n';
    }
    return 0;
}

LGP14969 They'll lead me to you

原题链接:They'll lead me to you

分析

打一个 O ( n 2 V ) O(n^2V) O(n2V) 的暴力,然后拼一下部分分,轻松拿下 27 p t s 27pts 27pts!

好的,暴力打完了。

那么,我们发现一些事情就是,第一,值域 V ≤ 2 n V\le 2n V≤2n,其次,由于 a i = min ⁡ ( a i , mex ⁡ ) , i ∈ l , r a_i=\min(a_i,\operatorname{mex}),i\inl,r ai=min(ai,mex),i∈l,r,而且, mex ⁡ \operatorname{mex} mex 恰好大概就是两块拼起来的,就是一部分是 0 , a j 0,a_j 0,aj, 另一部分是 a t , a k a_t,a_k at,ak,前面是连续的。


我大胆猜测,就是每次选择 1 , n 1,n 1,n 可能是优的。不过这个有点糙,需要一些进一步的思考。好像 0 0 0 把序列分成了一些块,所以,我们......还是优的!所以说,思考一下,除了样例 1 1 1 的 hack,其他的都可能在这个里面实现。


确实骗过了一些点,但是,由于捆包,所以,没有获得更高的分数。

由于以上关键的观察 V ≤ 2 n V\le 2n V≤2n,所以,我们直接枚举 mex ⁡ \operatorname{mex} mex,对于不含 mex ⁡ \operatorname{mex} mex 的区间,扔进桶里,树状数组直接做就行了。


这么牛?就是说,因为每次取 min ⁡ \min min,所以,直接做有正确性。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500005;
int n, a[N];
bool vis[N << 1];
int c1[N], c2[N];
vector<int> pos[N << 1];
void add(int x){
    for (int i = x; i <= n; i += i & (-i)){
        c1[i]++;
        c2[i] += a[x];
    }
}
int query1(int x){
    int res = 0;
    for (int i = x; i; i -= i & (-i)){
        res += c1[i];
    }
    return res;
}
int query2(int x){
    int res = 0;
    for (int i = x; i; i -= i & (-i)){
        res += c2[i];
    }
    return res;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	bool subA = true, subB = true;
	for (int i = 1; i <= n; i++){
		cin >> a[i];
		if (a[i] == 0){
			subA = false;
		}
		if (i >= 3 && a[i] == 0){
			subB = false;
		}
	}
	if (n <= 300){
		int mn = 0;
		for (int i = 1; i <= n; i++){
			mn += a[i];
		}
		int resl, resr;
		for (int l = 1; l <= n; l++){
			for (int r = l; r <= n; r++){
				memset(vis, false, sizeof(vis));
				for (int i = l; i <= r; i++){
					vis[a[i]] = true;
				}
				int mex = 0;
				for (int i = 0; i <= n * 2; i++){
					if (!vis[i]){
						mex = i;
						break;
					}
				}
				int sum = 0;
				for (int i = 1; i <= n; i++){
					if (i >= l && i <= r){
						sum += min(mex, a[i]);
					}
					else{
						sum += a[i];
					}
				}
				if (mn > sum){
					mn = sum;
					resl = l;
					resr = r;
				}
			}
		}
		cout << mn;
		return 0;
	}
	if (subA){
		cout << 0;
		return 0;
	}
	if (a[2] == 0 && subB){
		for (int i = 1; i <= n; i++){
			vis[a[i]] = true;
		}
		int mex;
		for (int i = 0; i <= n * 2; i++){
			if (!vis[i]){
				mex = i;
				break;
			}
		}
		cout << min({a[1], mex * n});
		return 0;
	}
    int sum = 0;
	for (int i = 1; i <= n; i++){
	    sum += a[i];
	    pos[a[i]].push_back(i);
	}
	int ans = 0x3f3f3f3f3f3f3f3f;
	for (int mex = n * 2; mex >= 0; mex--){
	    if (pos[mex].empty()){
	        ans = min(ans, sum - query2(n) + query1(n) * mex);
	        continue;
	    }
	    ans = min(ans, sum - query2(pos[mex][0]) + query1(pos[mex][0]) * mex);
	    for (int i = 0; i < (int)pos[mex].size(); i++){
	        if (i == (int)pos[mex].size() - 1){
	            ans = min(ans, sum - (query2(n) - query2(pos[mex][i])) + (query1(n) - query1(pos[mex][i])) * mex);
	        }
	        else{
	            ans = min(ans, sum - (query2(pos[mex][i + 1] - 1) - query2(pos[mex][i])) + (query1(pos[mex][i + 1] - 1) - query1(pos[mex][i])) * mex);
	        }
	        add(pos[mex][i]);
	    }
	}
	cout << ans;
}

LGP8955 「VUSC」Card Tricks

原题链接:「VUSC」Card Tricks

分析

尝试在半个小时内将其击败......或者我被击败😭


假做法被 LYX 骂了😭

难道说我在一个 d e l t a delta delta 上做吗?


看题解了😭

由于每个数不会变小,所以在时间轴上二分就行了😭

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000005;
int n, q, P;
int a[N];
int head[N], tot;
struct edge{
	int to, nxt, val;
}e[N << 1];
void add(int u, int v, int w){
	e[++tot].to = v;
	e[tot].nxt = head[u];
	e[tot].val = w;
	head[u] = tot;
}
int tr[N << 2];
struct segtree{
	void pushup(int p){
		tr[p] = tr[p << 1] | tr[p << 1 | 1];
	}
	void modify(int p, int l, int r, int s, int val){
		if (l == r){
			tr[p] = val;
			return ;
		}
		int mid = (l + r) >> 1;
		if (s <= mid)
			modify(p << 1, l, mid, s, val);
		else
			modify(p << 1 | 1, mid + 1, r, s, val);
		pushup(p);
	}
	int query(int p, int l, int r, int val){
		if (l == r){
			if ((val | tr[p]) <= P)
				return -1;
			return l;
		}
		int mid = (l + r) >> 1;
		if ((val | tr[p << 1]) > P)
			return query(p << 1, l, mid, val);
		else
			return query(p << 1 | 1, mid + 1, r, val | tr[p << 1]);
	}
}T;
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> q >> P;
	for (int i = 1; i <= n; i++){
		cin >> a[i];
	}
	for (int cs = 1, l, r, x; cs <= q; cs++){
		cin >> l >> r >> x;
		add(l, cs, x);
		add(r + 1, cs, 0);
	}
	for (int u = 1; u <= n; u++){
		for (int i = head[u]; i; i = e[i].nxt){
			T.modify(1, 1, q, e[i].to, e[i].val);
		}
		cout << T.query(1, 1, q, a[u]) << " ";
	}
}
相关推荐
hansang_IR2 小时前
【题解】[JSOI2016] 扭动的回文串
c++·算法
Lyyaoo.2 小时前
【二分查找】【困难】寻找两个有序数组的中位数
java·数据结构·算法
有点。2 小时前
C++03阶段练习(一)
数据结构·c++
刃神太酷啦2 小时前
Redis 核心进阶:哨兵、集群、缓存问题与分布式锁详解----《Hello Redis!》(6)
linux·c语言·数据库·c++·redis·分布式·缓存
郝学胜-神的一滴3 小时前
C++20模板元编程 01:从零吃透模板核心底层逻辑
开发语言·c++·vscode·程序人生·开源
kevin_kang3 小时前
第05章 实时语音识别的 ASR 原理
算法
kevin_kang3 小时前
第04章 断句原理:本地 VAD 与神经网络 VAD
算法
kevin_kang3 小时前
第06章 文字转语音的 TTS 原理
算法
6Hzlia3 小时前
【Classic 150 刷题计划】 LeetCode 123. 买卖股票的最佳时机 III | C++ 状态机动态规划与通用交易拓展
c++·算法·leetcode