日常训练2025-1-17

日常训练2025-1-17

rating:1500

https://codeforces.com/problemset/problem/2007/C

思路(裴蜀定理)

碰到要么加a 要么加 b 的题一定要想到裴蜀定理, ax + by = gcd(a, b)。即每个数可以加减k*gcd(a, b)。

所以我们可以把每个数都调整到只相差小于gcd(a, b)的范围内。这样会贡献一次答案。

然后每个数都可以做一次最大值和最小值,枚举一遍再求答案即可。

代码

#include <bits/stdc++.h>
typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;
void solve(){
int n, a, b;
std::cin >> n >> a >> b;
int d = std::gcd(a, b);
std::vector<int> v(n);
for (int i = 0; i < n; i++){
std::cin >> v[i];
v[i] %= d;
}
std::sort(v.begin(), v.end());
int ans = v[n-1] - v[0];
for (int i = 1; i < n; i++){
ans = std::min(ans, v[i-1]+d-v[i]);
}
std::cout << ans << '\n';
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
int t = 1, i;
std::cin >> t;
for (i = 0; i < t; i++){
solve();
}
return 0;
}

D. Maximize the Root

rating:1500

https://codeforces.com/problemset/problem/1997/D

思路(树形DP)

明显不能贪心。考虑 dp。

我们要让根节点尽可能大,那么就要让根节点能进行的操作数尽可能多。

而操作数取决于子树中权值最小的点的权值

dpi 表示 i 的子树操作完之后最小值的最大值。

转移时,令 mn 为 x 的儿子节点的 dp 值的最小值,分类讨论:

  • mn<ax。此时操作只会让最小值更小。dpx=mn
  • mn≥ax。此时我们可以操作直到 mn≤ax,这样最小值会变大。dpx=(a[x]+mn)/2

注意我们是要让根节点权值最大,所以在根节点上不需要考虑子树最小值最大,直接无脑操作即可。

代码

#include <bits/stdc++.h>
typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;
void solve(){
int n;
std::cin >> n;
std::vector<i64> b(n+1, 0);
std::vector<i64> fa(n+1, 0);
std::vector g(n+1, std::vector<i64>());
std::vector<i64> a(n+1, 0);
for (int i = 1; i <= n; i++) std::cin >> a[i];
for (int i = 2; i <= n; i++){
std::cin >> fa[i];
g[fa[i]].push_back(i);
g[i].push_back(fa[i]);
}
auto dfs = [&](auto self, i64 cur) -> void {
i64 minn = 1000000001;
for (auto to : g[cur]){
if (to == fa[cur]) continue;
self(self, to);
minn = std::min(minn, b[to]);
}
if (minn == 1000000001){
b[cur] = a[cur];
return;
}
if (cur == 1){
a[cur] += minn;
return;
}
if (a[cur] < minn) b[cur] = (a[cur] + minn) / 2;
else b[cur] = minn;
};
dfs(dfs, 1);
std::cout << a[1] << '\n';
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
int t = 1, i;
std::cin >> t;
for (i = 0; i < t; i++){
solve();
}
return 0;
}

C. Have Your Cake and Eat It Too

rating:1400

https://codeforces.com/problemset/problem/1983/C

思路(暴力枚举)

首先 3! 枚举下从前往后的每一段分别是由哪个人拿走

通过 two pointers 枚举中间那段的极小的合法区间,然后检验前后缀是否合法即可

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin >> n;
int a[n+1], b[n+1], c[n+1];
int sum = 0;
for(int i = 1;i<=n;i++)
{
cin >> a[i];
sum += a[i];
}
sum = (sum+2)/3;//题中说了上限除法
for(int i = 1;i<=n;i++)
{
cin >> b[i];
}
for(int i = 1;i<=n;i++)
{
cin >> c[i];
}
vector<int> p1(n+5), p2(n+5), p3(n+5);//正序前缀和
vector<int> s1(n+5), s2(n+5), s3(n+5);//倒序前缀和
for(int i = 1; i <= n; i++)
{
p1[i] = p1[i-1] + a[i];
p2[i] = p2[i-1] + b[i];
p3[i] = p3[i-1] + c[i];
}
for(int i = n; i >= 1; i--)
{
s1[i] = s1[i+1] + a[i];
s2[i] = s2[i+1] + b[i];
s3[i] = s3[i+1] + c[i];
}
//a b c
int i = 1, j = n;
while(p1[i-1] < sum && i <= n)
{
i++;
}
while(s3[j+1] < sum && j >= 1)
{
j--;
}
if(i <= j && p2[j]-p2[i-1] >= sum)
{
cout << 1 << ' ' << i-1 << ' ' << i << ' ' << j << ' ' << j+1 << ' ' << n << endl;
continue;
}
// a c b
i = 1, j = n;
while(p1[i-1] < sum && i <= n)
{
i++;
}
while(s2[j+1] < sum && j >= 1)
{
j--;
}
if(i <= j && p3[j]-p3[i-1] >= sum)
{
cout << 1 << ' ' << i-1 << ' ' << j+1 << ' ' << n << ' ' << i << ' ' << j << endl;
continue;
}
// b c a
i = 1, j = n;
while(p2[i-1] < sum && i <= n)
{
i++;
}
while(s1[j+1] < sum && j >= 1)
{
j--;
}
if(i <= j && p3[j]-p3[i-1] >= sum)
{
cout << j+1 << ' ' << n << ' ' << 1 << ' ' << i-1 << ' ' << i << ' ' << j << endl;
continue;
}
// b a c
i = 1, j = n;
while(p2[i-1] < sum && i <= n)
{
i++;
}
while(s3[j+1] < sum && j >= 1)
{
j--;
}
if(i <= j && p1[j]-p1[i-1] >= sum)
{
cout << i << ' ' << j << ' ' << 1 << ' ' << i-1 << ' ' << j+1 << ' ' << n << endl;
continue;
}
// c a b
i = 1, j = n;
while(p3[i-1] < sum && i <= n)
{
i++;
}
while(s2[j+1] < sum && j >= 1)
{
j--;
}
if(i <= j && p1[j]-p1[i-1] >= sum)
{
cout << i << ' ' << j << ' ' << j+1 << ' ' << n << ' ' << 1 << ' ' << i-1 << endl;
continue;
}
// c b a
i = 1, j = n;
while(p3[i-1] < sum && i <= n)
{
i++;
}
while(s1[j+1] < sum && j >= 1)
{
j--;
}
if(i <= j && p2[j]-p2[i-1] >= sum)
{
cout << j+1 << ' ' << n << ' ' << i << ' ' << j << ' ' << 1 << ' ' << i-1 << endl;
continue;
}
cout << -1 << endl;
}
return 0;
}

D. GCD-sequence

rating:1400

https://codeforces.com/problemset/problem/1980/D

思路(暴力枚举)

这种操作后只会使小范围内的关系发生变化的题一般都是暴力枚举操作位置。

一个数被删除只会使相邻的逆序关系的减少和新的逆序关系的增加,对其他位置没有影响。所以我们只需要暴力枚举每个数被删除的时候的情况满不满足题意。

代码

#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
std::vector<int> b(n - 1);
for (int i = 0; i < n - 1; i++) {
b[i] = std::gcd(a[i], a[i + 1]);
}
int bad = 0;
for (int i = 1; i < n - 1; i++) {
bad += b[i] < b[i - 1];
}
int ans = 0;
if (bad - (b[1] < b[0]) == 0) {
ans = 1;
}
if (bad - (b[n - 2] < b[n - 3]) == 0) {
ans = 1;
}
for (int i = 1; i < n - 1; i++) {
int tmp = bad;
tmp -= b[i] < b[i - 1];
int g = std::gcd(a[i - 1], a[i + 1]);
if (i - 1 > 0) {
tmp -= b[i - 1] < b[i - 2];
tmp += g < b[i - 2];
}
if (i + 1 < n - 1) {
tmp -= b[i + 1] < b[i];
tmp += b[i + 1] < g;
}
if (tmp == 0) {
ans = 1;
}
}
if (ans) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}

C猪猪养成计划1

https://ac.nowcoder.com/acm/contest/99785/C

思路(类模拟)

优化思路看看代码就能懂

代码

#include <bits/stdc++.h>
typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;
void solve(){
int n, q;
std::cin >> n >> q;
std::vector<int> vis(n+1), next(n+1);
int cnt = 0;
for (int i = 0; i < q; i++){
int key; std::cin >> key;
if (key == 1){
int l, r;
std::cin >> l >> r;
int i = l;
while (i <= r){
if (vis[i] != 0) i = next[i];
else{
vis[i] = ++cnt;
next[i] = r+1;
i++;
}
}
}else{
int x;
std::cin >> x;
std::cout << vis[x] << '\n';
}
}
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
int t = 1, i;
for (i = 0; i < t; i++){
solve();
}
return 0;
}

本文作者:califeee

本文链接:https://www.cnblogs.com/califeee/p/18676644

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   califeee  阅读(2)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.