2026-10-22~23 hetao1733837 的刷题记录

LGP17537 JAG 2026 Summer Camp #1 Rabbit, Rabbit, Rabbit

原题链接:JAG 2026 Summer Camp #1 Rabbit, Rabbit, Rabbit

分析

别急,如果说每一次我只让一些兔子向右,不让一些兔子向左,那代价岂不是 0 0 0?

哦,看懂了......好吧,竟然是总共吗?

那么,既然如此,我们就需要......只要把位于同一位置的分开是不是就可以了?

不是,我没看出来在哪贪心......


我需要一个样例解释......

不是,你这个真的有问题啊😭


每一只?哦,那我懂了......

那么,每只兔子最好都移向距离最近的......哦不不不,这个不一定有解。


那么,应该就是先尽可能一个方向,然后再进行一些返回,就可以保证最小了?

所以说,先排序,然后,我们发现,最小距离公式就是:

∑ 1 ≤ i < j ≤ n ∣ d i − d j ∣ \sum\limits_{1\le i<j\le n}|d_i-d_j| 1≤i<j≤n∑∣di−dj∣

其中, d d d 是与目标的距离。


我再也不用 AI 写代码了。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 300005;
const int inf = 1e18;
int T;
int n, x[N], y[N];
int tmpx[N], tmpy[N];
int calc(){
    sort(tmpx + 1, tmpx + n + 1);
    for (int i = 2; i <= n; i++)
        tmpx[i] -= tmpx[1];
    tmpx[1] = 0;
    int res = 0;
    for (int i = 1; i <= n; i++){
        tmpy[i] = tmpy[i - 1] + tmpx[i];
    }
    for (int i = 2; i <= n; i++){
        res += i * tmpx[i] - tmpy[i];
    }
    return res / 2;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> T;
    for (int cs = 1; cs <= T; cs++){
        cin >> n;
        for (int i = 1; i <= n; i++) 
            cin >> x[i];
        for (int i = 1; i <= n; i++) 
            cin >> y[i];
        sort(x + 1, x + n + 1);
        sort(y + 1, y + n + 1);
        vector<int> x00, x11;
        vector<int> y00, y11;
        for (int i = 1; i <= n; i++){
            if (x[i] & 1) 
                x11.push_back(x[i]);
            else          
                x00.push_back(x[i]);
        }
        for (int i = 1; i <= n; i++){
            if (y[i] & 1) 
                y11.push_back(y[i]);
            else          
                y00.push_back(y[i]);
        }
        int ans = inf;
        if (x00.size() == y00.size() && x11.size() == y11.size()){
            int tot = 0;
            for (int i = 0; i < (int)x00.size(); i++){
                tmpx[++tot] = x00[i] - y00[i];
            }
            for (int i = 0; i < (int)x11.size(); i++){
                tmpx[++tot] = x11[i] - y11[i];
            }
            ans = min(ans, calc());
        }
        if (x11.size() == y00.size() && x00.size() == y11.size()){
            int tot = 0;
            for (int i = 0; i < (int)x00.size(); i++){
                tmpx[++tot] = x00[i] - y11[i];
            }
            for (int i = 0; i < (int)x11.size(); i++){
                tmpx[++tot] = x11[i] - y00[i];
            }
            ans = min(ans, calc());
        }
        if (ans == inf)
            cout << -1 << "\n";
        else
            cout << ans << "\n";
    }
}

LGP14015 ICPC 2024 Nanjing R 生日礼物

原题链接:ICPC 2024 Nanjing R 生日礼物

分析

思考一下......

我们是不是直接设 d p i , 0 / 1 dp_{i,0/1} dpi,0/1 表示考虑到了第 i i i 位,上一次操作留下来的是 0 / 1 0/1 0/1 的最小长度(这个,我们不用管,就是说,贪心地转移,如果这次可以删除,则立刻删除,从 d p i − 2 dp_{i-2} dpi−2 转移,如果当前是 2 2 2,则可以两个同时进行,直接就做完了,嘻嘻......)

真的假的?先自己写吧。


写到一半上生物课了,剖了一只雄性蛔虫......然后,好像有点分错了,把肠子给整成两截分开展示了,是一个重要的错误......呃......中午就先吃米饭吧,晚上再吃面条,快考试了,考试的时候中午我一般都吃米饭,需要提前一周适应一下。


发现这个直接可以按照奇偶位置贪心......我发现了吗?但是 DP 的复杂度也是对的,所以,我觉得,其实不是很大的问题?

正解

DP 版
cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005;
int T;
string s;
int a[N];
int dp[N][2][2];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> T;
	for (int cs = 1; cs <= T; cs++){
		cin >> s;
		int top = 0;
		for (int i = 0; i < (int)s.length(); i++){
			int x = s[i] - '0';
			if (top > 0 && x <= 1 && x == a[top]){
				top--;
			}
			else{
				top++;
				a[top] = x;
			}
		}
		if (top == 0){
			cout << 0 << '\n';
			continue;
		}
		for (int i = 0; i <= top; i++){
			dp[i][0][0] = -1;
			dp[i][0][1] = -1;
			dp[i][1][0] = -1;
			dp[i][1][1] = -1;
		}
		if (a[1] == 0 || a[1] == 2){
			dp[1][0][0] = 1;
			dp[1][0][1] = 1;
		}
		if (a[1] == 1 || a[1] == 2){
			dp[1][1][0] = 1;
			dp[1][1][1] = 1;
		}
		for (int i = 2; i <= top; i++){
			if (a[i] == 0 || a[i] == 2){
				if (dp[i - 1][1][1] != -1){
					dp[i][0][1] = dp[i - 1][1][1] + 1;
				}
				else if (dp[i - 1][0][0] == 0){
					dp[i][0][1] = 1;
				}
				if (dp[i - 1][0][0] == 0){
					dp[i][0][0] = 1;
				}
				else if (dp[i - 1][1][0] != -1){
					dp[i][0][0] = dp[i - 1][1][0] + 1;
				}
				if (dp[i - 1][0][1] >= 1){
					dp[i][1][1] = dp[i - 1][0][1] - 1;
				}
				if (dp[i - 1][0][0] >= 1){
					dp[i][1][0] = dp[i - 1][0][0] - 1;
				}
				else if (dp[i - 1][0][1] >= 1){
					dp[i][1][0] = (dp[i - 1][0][1] & 1) ^ 1;
				}
			}
			if (a[i] == 1 || a[i] == 2){
				if (dp[i - 1][1][1] >= 1){
					if (dp[i][0][1] == -1){
						dp[i][0][1] = dp[i - 1][1][1] - 1;
					}
					else{
						dp[i][0][1] = max(dp[i][0][1], dp[i - 1][1][1] - 1);
					}
				}
				if (dp[i - 1][1][0] >= 1){
					if (dp[i][0][0] == -1){
						dp[i][0][0] = dp[i - 1][1][0] - 1;
					}
					else{
						dp[i][0][0] = min(dp[i][0][0], dp[i - 1][1][0] - 1);
					}
				}
				else if (dp[i - 1][1][1] >= 1){
					int val = (dp[i - 1][1][1] & 1) ^ 1;
					if (dp[i][0][0] == -1){
						dp[i][0][0] = val;
					}
					else{
						dp[i][0][0] = min(dp[i][0][0], val);
					}
				}
				if (dp[i - 1][0][1] != -1){
					if (dp[i][1][1] == -1){
						dp[i][1][1] = dp[i - 1][0][1] + 1;
					}
					else{
						dp[i][1][1] = max(dp[i][1][1], dp[i - 1][0][1] + 1);
					}
				}
				else if (dp[i - 1][1][0] == 0){
					if (dp[i][1][1] == -1){
						dp[i][1][1] = 1;
					}
					else{
						dp[i][1][1] = max(dp[i][1][1], 1LL);
					}
				}
				if (dp[i - 1][1][0] == 0){
					if (dp[i][1][0] == -1){
						dp[i][1][0] = 1;
					}
					else{
						dp[i][1][0] = min(dp[i][1][0], 1LL);
					}
				}
				else if (dp[i - 1][0][0] != -1){
					if (dp[i][1][0] == -1){
						dp[i][1][0] = dp[i - 1][0][0] + 1;
					}
					else{
						dp[i][1][0] = min(dp[i][1][0], dp[i - 1][0][0] + 1);
					}
				}
			}
		}
		int ans = -1;
		if (dp[top][0][0] != -1){
			if (ans == -1 || dp[top][0][0] < ans){
				ans = dp[top][0][0];
			}
		}
		if (dp[top][1][0] != -1){
			if (ans == -1 || dp[top][1][0] < ans){
				ans = dp[top][1][0];
			}
		}
		cout << ans << '\n';
	}
	return 0;
}
贪心版
cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int T;
string s;
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> T;
	for (int cs = 1; cs <= T; cs++){
		cin >> s;
		int cnt0[2] = {0, 0};
		int cnt1[2] = {0, 0};
		int cnt2 = 0;
		for (int i = 0; i < (int)s.length(); i++){
			int p = i % 2;
			if (s[i] == '0') 	
				cnt0[p]++;
			else if (s[i] == '1') 
				cnt1[p]++;
			else 
				cnt2++;
		}
		int rem0 = abs(cnt0[0] - cnt0[1]);
		int rem1 = abs(cnt1[0] - cnt1[1]);
		int rem = rem0 + rem1;
		int ans;
		if (cnt2 >= rem) 
			ans = (cnt2 - rem) % 2;
		else 
			ans = rem - cnt2;
		cout << ans << '\n';
	}
	return 0;
}

QOJ8635 圆

原题链接:圆

分析

拆拆你的贡献!

思考一下具体做法......

就是说,设一个期望 DP 吧!

我们设 d p i , j dp_{i,j} dpi,j 表示有 i i i 个已经被选过,有 j j j 个已经被染黑的期望......

那么怎么转移呢?

我是不是还需要一个有多少白色没有被选择?如果有这个,是不是可以推出来别的?

不妨先将其放入状态,即 d p i , j , k dp_{i,j,k} dpi,j,k 表示已经选了 i i i 个,有 j j j 个已经被染黑,有 k k k 个白色未选的期望。

那么,答案是

a n s = ∑ d p i , n , k ans=\sum dp_{i,n,k} ans=∑dpi,n,k

然而,这个还是不好做......

其实,期望的本质还是组合数学,你需要知道每一个的情况数。


感觉脖子快断了。


本质就是选某一个位置或不选某一个位置,那么,这种 临近 让我们想到了斐波那契数列,所以,我们直接做就行了。

呃......我真的读懂题了吗?

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
#define mod 998244353
using namespace std;
const int N = 5005;
int n;
int dp[N];
int qpow(int a, int b){
	int res = 1;
	while (b){
		if (b & 1)
			res = res * a % mod;
		a = a * a % mod;
		b >>= 1; 
	}
	return res;
}
int fac[N], inv[N];
int C(int n, int m){
	if (n < 0 || m < 0 || n < m)
		return 0;
	return fac[n] * inv[n - m] % mod * inv[m] % mod;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	fac[0] = 1;
	for (int i = 1; i < N; i++){
		fac[i] = fac[i - 1] * i % mod;
	}
	inv[N - 1] = qpow(fac[N - 1], mod - 2);
	for (int i = N - 2; i >= 0; i--){
		inv[i] = inv[i + 1] * (i + 1) % mod;
	}
	dp[0] = 1;
	int ans = n;
	for (int i = 1; i < n; i++){
		for (int j = n; j >= 0; j--){
			if (j >= 1) 
				dp[j] = (dp[j] + dp[j - 1]) % mod;
			if (j >= 2) 
				dp[j] = (dp[j] + dp[j - 2]) % mod;
		}	
		ans = (ans + mod - dp[n - i] * qpow(C(n - 1, i - 1), mod - 2) % mod) % mod;
	}
	cout << ans;
	return 0;
}

CF1270G Subset with Zero Sum

原题链接:G. Subset with Zero Sum

分析

我总觉得有什么奇妙的性质......

如果说所有数都取到下界,那么,必然有解;如果所有数取到上界,也必然有解......

然而,这个并不一定是全部上界或下界,所以,即使是前缀和......

别急,你会发现,所有数的取值都是数轴上的滑动的窗口......那又能说明什么呢?

给了,不想了,看看题解......

啥叫图论建模?

天啊,对于 n − i ≤ a i ≤ i − 1 n-i\le a_i\le i-1 n−i≤ai≤i−1,移项可得 1 ≤ i − a i ≤ n 1\le i-a_i\le n 1≤i−ai≤n,所以,将 a i a_i ai 连向 i − a i i-a_i i−ai,则找到环即可。


我严格 O ( n ) O(n) O(n) 还卡我?

正解

cpp 复制代码
#include <bits/stdc++.h>
// #define int long long
using namespace std;
const int N = 1000005;
int t;
int n, a[N];
int to[N];
bool vis[N];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> t;
	for (int cs = 1; cs <= t; cs++){
		cin >> n;
		// memset(vis, false, sizeof(vis));
		for (int i = 1; i <= n; i++){
			cin >> a[i];
			to[i] = i - a[i];
			vis[i] = false;
		}
		int v = 1;
		while (!vis[v]){
			vis[v] = true;
			v = to[v];
		}
		vector<int> ans;
		ans.push_back(v);
		v = to[v];
		while (v != ans[0]){
			ans.push_back(v);
			v = to[v];
		}
		cout << ans.size() << '\n';
		for (auto v : ans)
			cout << v << " ";
		cout << '\n';
	} 
} 

LGP11801 【MX-X9-T5】『GROI-R3』Star Trip

原题链接:【MX-X9-T5】『GROI-R3』Star Trip

分析

昨天晚上想到了一个 O ( q n 2 ) O(qn^2) O(qn2) 的 DP......

难道他真的有 K r u s k a l Kruskal Kruskal 重构树的性质!


我们可以进行一些处理,进行倍增,考虑从 u u u 出发,走若干步可以到达的点的编号的最大值。

对于依次询问,我们需要知道点 u u u 到 v v v 路径上最大编号的最小值,也就是瓶颈最短路......

呃,啊吧啊吧......

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 400005;
int n, m, q;
vector<int> e[N], tr[N];
struct node{
	int u, v, val;
}inp[N];
bool cmp(node tmp1, node tmp2){
	return tmp1.val < tmp2.val;
}
int fa[N];
int f[N][22];
int de[N];
int find(int x){
	return x == fa[x] ? fa[x] : fa[x] = find(fa[x]);
}
int tot;
int val[N];
int mx[N], pre[N];
int son[N][22];
void merge(int x, int y, int w){
	int fx = find(x), fy = find(y);
	if (fx == fy)
		return ;
	val[++tot] = w;
	fa[tot] = tot;
	fa[fx] = fa[fy] = tot;
	tr[tot].push_back(fx);
	tr[tot].push_back(fy);
}
void dfs(int u, int pa){
	f[u][0] = pa;
	de[u] = de[pa] + 1;
	for (auto v : tr[u]){
		if (v == pa)
			continue;
		dfs(v, u);
	}
}
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 >> m >> q;
	tot = n;
	for (int i = 1, u, v; i <= m; i++){
		cin >> u >> v;
		inp[i].u = u;
		inp[i].v = v;
		inp[i].val = max(u, v);
		e[u].push_back(v);
		e[v].push_back(u);
	}
	sort(inp + 1, inp + m + 1, cmp);
	for (int i = 1; i <= n; i++){
		fa[i] = i;
	}
	for (int i = 1; i <= m; i++){
		merge(inp[i].u, inp[i].v, inp[i].val);
	}
	dfs(tot, 0);
	for (int i = 1; i <= 20; i++){
		for (int u = 1; u <= tot; u++){
			f[u][i] = f[f[u][i - 1]][i - 1];
		}
	}
	for (int i = 1; i <= n; i++){
		fa[i] = i;
	}
	for (int i = 1; i <= n; i++){
		mx[i] = i;
	}
	for (int u = 1; u <= n; u++){
		for (auto v : e[u]){
			if (v < u)
				mx[u] = max(mx[u], pre[find(v)]);
			else
				mx[u] = max(mx[u], v);
		}
		pre[u] = mx[u];
		for (auto v : e[u]){
			if (v < u){
				int fu = find(u), fv = find(v);
				if (fu == fv)
					continue;
				pre[fv] = max(pre[fv], pre[fu]);
				fa[fu] = fv;
			}
		}
	}
	for (int i = 1; i <= n; i++)
		son[i][0] = mx[i];
	for (int i = 1; i <= 20; i++){
		for (int u = 1; u <= n; u++){	
			son[u][i] = son[son[u][i - 1]][i - 1];
		}
	}
	for (int cs = 1, s, t; cs <= q; cs++){
		cin >> s >> t;
		int tmp = val[LCA(s, t)];
		if (s >= tmp){
			cout << 1 << "\n";
			continue;
		}
		int ans = 0;
		for (int i = 20; i >= 0; i--){
			if (son[s][i] < tmp){
				ans += (1 << i);
				s = son[s][i];
			}
		}
		cout << ans + 2 << "\n";
	}
}

LGP15268 「UTOI 1C」Delirium Of Aeons

原题链接:「UTOI 1C」Delirium Of Aeons

分析

仅就贪心的角度,我们对于一个分叉的位置,思考直接开切,这样可以消耗一些 m m m。

剩下的,对于没有兄弟的儿子,那没有办法了,只能对答案进行加了。

天啊,不会是什么 m − n + ∑ u d e g u = 1 m-n+\sum_udeg_u=1 m−n+∑udegu=1。

这个好像不对......但是,为什么还有 T L E TLE TLE?


居然是长链剖分......


我们应该好好想部分分的......我们发现,在链上,除了两端,其他位置都随便做......这提醒我们,虽然我们之前觉得叶子很好做,但是,在链上才是"归宿",所以说,我们进行长链剖分,这样就尽可能减少了相邻为 1 1 1,然后就做完了......

正解

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int T;
int n, m;
vector<int> e[N];
int de[N];
int son[N];
int down[N];
int f[N];
vector<int> a;
vector<tuple<int, int, int>> stk1;
vector<pair<int, int>> stk2;
int prevn = 0;
int dfs1(int root){
    stk1.clear();
    stk1.emplace_back(root, 0, 0);
    int far = root;
    while (!stk1.empty()){
        auto [u, pa, state] = stk1.back();
        stk1.pop_back();
        if (state == 0){
            f[u] = pa;
            de[u] = de[pa] + 1;
            if (de[u] > de[far])
                far = u;
            stk1.emplace_back(u, pa, 1);
            for (auto v : e[u]){
                if (v == pa)
                    continue;
                stk1.emplace_back(v, u, 0);
            }
        }
        else{
            int best = 0, bs = 0;
            for (auto v : e[u]){
                if (v == pa)
                    continue;
                if (down[v] > best){
                    best = down[v];
                    bs = v;
                }
            }
            son[u] = bs;
            down[u] = max(de[u], best);
        }
    }
    return far;
}
void dfs2(int root){
    stk2.clear();
    stk2.emplace_back(root, root);
    while (!stk2.empty()){
        auto [u, tp] = stk2.back();
        stk2.pop_back();
        int cur = u;
        while (cur){
            for (auto v : e[cur]){
                if (v == f[cur] || v == son[cur])
                    continue;
                stk2.emplace_back(v, v);
            }
            int nxt = son[cur];
            if (!nxt)
                a.push_back(de[cur] - de[tp] + 1);
            cur = nxt;
        }
    }
}
void init(){
    a.clear();
    for (int i = 1; i <= prevn; i++)
        e[i].clear();
    prevn = n;
}
void sort_a(){
    static int buc[N];
    static int last_max = 0;
    for (int i = 0; i <= last_max; i++)
        buc[i] = 0;
    int mx = 0;
    for (auto v : a){
        buc[v]++;
        if (v > mx)
            mx = v;
    }
    last_max = mx;
    a.clear();
    for (int i = mx; i >= 1; i--){
        for (int j = 1; j <= buc[i]; j++){
            a.push_back(i);
        }
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> T;
    for (int cs = 1; cs <= T; cs++){
        cin >> n >> m;
        init();
        for (int i = 1, u, v; i < n; i++){
            cin >> u >> v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        int rt = dfs1(1);
        dfs1(rt);
        dfs2(rt);
        sort_a();
        int ans = 1;
        for (auto v : a){
            ans++;
            if (m > v)
                m -= v;
            else
                break;
        }
        cout << ans << "\n";
    }
}

不是哥们,卡我常,还卡我空间?

LGT618706 「ZYZ 2025」地铁

原题链接:「ZYZ 2025」地铁

分析

我们只需要枚举最后是哪辆车到达终点即可。

正解

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005;
int n, S, E, v0;
struct node{
	int t, v;
}a[N];
bool cmp(node o, node p){
	return o.t < p.t;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
	cin >> n >> S >> E >> v0;
	int k = 0;
	for (int i = 1, x, y; i <= n; i++){
		cin >> x >> y;
		if (y >= v0){
			a[++k].t = x;
			a[k].v = y;
		}
	}
	sort(a + 1, a + k + 1, cmp);
	int d = E - S;
	double ans = (double)d / v0;
	for (int i = 1; i <= k; i++){
		double tim = (double)a[i].t + (double)E / a[i].v;
		ans = min(ans, tim); 
	}
	cout << fixed << setprecision(10) << ans << '\n';
    return 0;
}
相关推荐
java_nnnn1 小时前
LeetCode刷题滑动窗口(串联所有单词的子串+最小覆盖的子串)Java
算法·leetcode·职场和发展
王老师青少年编程2 小时前
2025年6月GESP真题及题解(C++三级): 选择题和判断题(题解)
c++·真题·gesp·答案·csp·三级·信奥赛
Navigator_Z2 小时前
LeetCode //C - 1301. Number of Paths with Max Score
c语言·算法·leetcode
卡卡~~3 小时前
“101计划”核心课程实践教材《数据实验结构指导-C++语言版》题目集
开发语言·c++·学习·算法
企业数字化笔记3 小时前
AI视频增强后画质怎么验收?插值基线、伪细节与帧间闪烁的检查方法
人工智能·算法·音视频
学逆向的4 小时前
C 内存属性
c语言·开发语言·算法·网络安全
lengxuenong4 小时前
树状数组 · 信息学学习手册1-求和3个模板
c++·学习·算法·青少年编程
ComputerInBook4 小时前
泛函分析概观
人工智能·算法·机器学习·泛函分析