Codeforces Round 1066 (Div. 1 + Div. 2) 做题记录

Dashboard - Codeforces Round 1066 (Div. 1 + Div. 2) - Codeforces

Problem - A - Codeforces

题意:
平衡数组定义为:若 \(x\) 存在,则存在 \(x\)\(x\),求给定数组至少删去多少数变成平衡数组。

题解:
\(cnt_x<x\),则删去 \(cnt_x\) 个,否则删去 \(cnt_x-x\) 个。

点击查看代码
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define siz(a) ((int)a.size())
// #define fi first
// #define se second
// #define double long double
// #define int ll

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 105;
int n, cnt[N];

void solve()
{
	n = read();
	for(int i = 1; i <= n; ++i)
	{
		int x = read();
		++cnt[x];
	}
	int ans = 0;
	for(int i = 0; i <= n; ++i)
	{
		if(cnt[i] >= i) ans += cnt[i] - i;
		else ans += cnt[i];
	}
	printf("%d\n", ans);
	for(int i = 0; i <= n; ++i) cnt[i] = 0;
}

signed main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

Problem - B - Codeforces

题意:
在一个二维平面上,最初只有 \((0,0)\) 是黑色。
给定操作串 \(S\),若 \(s_i=4\),则所以与黑色块四联通的白色块都会染成黑色。
\(s_i=8\),则所有与黑色块吧联通的白色块都是染成黑色。
求操作后 \((X,Y)\) 是否会染黑。

题解:
首先 \(X,Y\) 分别取绝对变成距离。
如果 \(s_i=4\),则 \(X-1\) 或者 \(Y-1\),贪心选取最大的。(或者 \(X,Y\) 已经成为0不再操作)。
如果 \(s_i=8\),则 \(X-1\)\(Y-1\),(或者 \(X,Y\) 已经成为0不再操作)。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define siz(a) ((int)a.size())
// #define fi first
// #define se second
// #define double long double
// #define int ll

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

void solve()
{
	int n = read(), x = read(), y = read();
	x = abs(x), y = abs(y);
	string s;
	cin >> s;
	for(int i = 0; i < siz(s); ++i)
	{
		if(s[i] == '4')
		{
			if(x > y)
			{
				--x;
			}else if(y > 0)
			{
				--y;
			}
		}else 
		{
			x = max(x - 1, 0);
			y = max(y - 1, 0);
		}
	}
	if(x == 0 && y == 0) printf("YES\n");
	else printf("NO\n");
}

signed main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

Problem - C - Codeforces

题意:
给定 \(n,k,q\),有 \(q\) 组限制 \((c,l,r)\),求长度为 \(n\) 的数组 \(a\),满足:

  • \(c=1\),则 \(\min(a_l,a_{l+1}\cdots,a_r)=k\)
  • \(c=2\),则 \(\text{mex}(a_l,a_{l+1},\cdots,a_r)=k\)
    保证有解,输出任意合法数组 \(a\)

题解:
被第 \(1\) 类区间覆盖的位置不能有小于 \(k\) 的数,需要有 \(k\);被第 \(2\) 类区间覆盖的位置需要有小于 \(k\) 的数,不能有\(k\)
对位置按照被哪类区间覆盖划分:

  • 被两类区间都覆盖:此时不能填小于等于 \(k\) 的数,填 \(k+1\)
  • 被第 \(1\) 类区间覆盖但不被第 \(2\) 类区间覆盖:无脑填 \(k\)
  • 不被任何区间覆盖:随便填,这里填 \(k+1\)
    对于不被第 \(1\) 类区间覆盖但被第 \(2\) 类区间覆盖的位置,它们需要满足对于任意第 \(2\) 类区间内,都有 \(0\sim k-1\)

由于题目保证有解,意味着每个第 \(2\) 类区间内都有至少 \(k\) 个这种位置,在这种位置中按顺序循环填写 \(0\sim k-1\) 一定满足条件。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define siz(a) ((int)a.size())
// #define fi first
// #define se second
// #define double long double
// #define int ll

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 105;
int n, K, Q;
vector< pii > p1, p2;
int v1[N], v2[N], a[N], vis[N];

void solve()
{
	n = read(), K = read(), Q = read();
	for(int i = 1; i <= n; ++i) v1[i] = 0, v2[i] = 0, a[i] = -1;
 	p1.clear(), p2.clear();
	for(int i = 1; i <= Q; ++i)
	{
		int c = read(), l = read(), r = read();
		if(c == 1) p1.emplace_back(pii(l, r));
		else p2.emplace_back(pii(l, r));
	}
	for(auto [l, r] : p1)
	{
		for(int i = l; i <= r; ++i) v1[i] = 1;
	}
	for(auto [l, r] : p2)
	{
		for(int i = l; i <= r; ++i) v2[i] = 1;
	}
	for(int i = 1; i <= n; ++i)
	{
		if(v1[i] && v2[i]) a[i] = K + 1;
		else if(v1[i] && !v2[i]) a[i] = K;
		else if(!v1[i] && !v2[i]) a[i] = K + 1;
	}
	int x = 0;
	for(int i = 1; i <= n; ++i)
	{
		if(a[i] == -1) a[i] = x++;
		x %= K;
	}

	for(int i = 1; i <= n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

signed main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

Problem - D - Codeforces

题意:
你知道你喜欢的选手的排名 \(p\) 满足 \(l\le p\le r\),有 \(n\) 次竞猜:

每次给定 \(a_i\),可以有三种选择:

  • 不参与本轮竞猜。
  • 声称 \(p\le a_i\),猜对了获得 \(|p-a_i|\) 分,猜错了损失 \(|p-a_i|\) 分。
  • 声称 \(p\ge a_i\),猜对了获得 \(|p-a_i|\) 分,猜错了损失 \(|p-a_i|\) 分。

求在最优的竞猜策略下,你至少能获得多少分数,初始 \(0\) 分。

题解:答案为 \(\min\limits_{l\le p\le r}\sum\limits_{i=1}^{n}c_i\times (p-a_i)\)\(c_i\in \{-1,0,1\}\)
注意到,对于较大的 \(a_i\),选取 \(a_i-p\) 比较优,对于较小的 \(a_i\),选取 \(p-a_i\) 比较优。
按照 \(a_i\) 从大到小排序,枚举 \(p\) 前面的系数,然后一对 \(a_i-p,p-a_j\) 会抵消 \(p\)\(i<j,a_i\ge a_j\) 一定产生正贡献,因此至多有一轮是不参与竞猜的。预处理前缀和即可 \(O(1)\) 计算。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define siz(a) ((int)a.size())
// #define fi first
// #define se second
// #define double long double
// #define int ll

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int n, l, r;
ll a[N], s[N];

void solve()
{
	n = read(), l = read(), r = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	sort(a + 1, a + n + 1);
	reverse(a + 1, a + n + 1);
	for(int i = 1; i <= n; ++i) s[i] = s[i - 1] + a[i];
	ll ans = -0x3f3f3f3f3f3f3f3f;
	for(ll K = -n; K <= n; K += 2)
	{
		ll y = (n + K) / 2, x = n - y; // 前x个取a-p
		ll sum = s[x] - (s[n] - s[x]);
		if(K < 0)
		{
			ans = max(ans, sum + K * r);
		}else
		{
			ans = max(ans, sum + K * l);
		}
	}
	if(n & 1)
	{
		int len = n / 2;
		ll sum = s[len] - (s[n] - s[len + 1]);
		ans = max(ans, sum);
	}
	printf("%lld\n", ans);
}

signed main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

Problem - E - Codeforces

题意:
若数字 \(x\) 出现次数超过 \(k\),则除第一个 \(x\) 外其他的 \(x\) 都加 \(1\),求会执行多少次操作。

题解:
求出每个数的出现次数 \(cnt_i\),答案即最长的 \([l,r]\) 满足:

\[\sum_{l\le j\le i}(cnt_{j}-1)\ge k,\forall i\in [l,r] \]

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define siz(a) ((int)a.size())
// #define fi first
// #define se second
// #define double long double
// #define int ll

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int n, K, a[N], cnt[N * 3];

void solve()
{
	n = read(), K = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	for(int i = n * 3; i >= 1; --i) cnt[i] = 0;
	for(int i = 1; i <= n; ++i) ++cnt[a[i]];
	int ans = 0, last = 0, sum = 0;
	for(int i = 1; i <= n * 3; ++i)
	{
		cnt[i] += last;
		if(cnt[i] > K)
		{
			++sum;
			ans = max(ans, sum);
			last = cnt[i] - 1;
		}else last = 0, sum = 0;
	}
	printf("%d\n", ans);
}

signed main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

Problem - F - Codeforces

题意:
定义每个关卡有难度 \(y\) 和技能点 \(l\),设上一个关卡的难度为 \(x\)

  • 若当前为第一关或者 \(x\ge y\),则消耗 \(l\) 的时间。
  • 否则消耗 \(l+1000\) 的时间。
    只有当关卡难度 \(y\) 等于你当前的技能点 \(s\) 时,可以使技能点增加 \(l\)\(s+l\to s\)

构造不超过 \(10^6\) 个关卡,要求消耗的总时间不超过 \(10^6\),且对于任意的初始技能点 \(s\in [1,n]\),都可以在通过所有关卡后不小于 \(n\)。本题的 \(n\) 固定为 \(250000\)

题解:

设二元组 \((y,l)\) 表示关卡的两个属性。

最朴素的想法是倒序关卡 \((n-1,1),(n-2,2)\cdots,(1,n-1)\),一步到达 \(n\),消耗 \(\frac{n(n-1)}{2}\) 的时间,考虑合并一些内容。

考虑线段树式的构造:构造关卡 \((n-1,1),(n-2,2),(n-3,1),(n-4,2)\cdots(3,1),(2,2),(1,1),\)\((n-2,2),(n-4,4),(n-6,2),(n-8,4)\cdots\),这样合并了很多路径,但仍然不优。

注意到相同难度的关卡重复出现一定不优,有没有避免重复难度的关卡的解法?

考虑树状数组式的构造:\((1,1),(3,1),\cdots,(n-1,1),(2,2),(6,2)\cdots(n-2,2),(4,4)\cdots\),这样保证了每种难度的关卡只出现一次,且都能达到 \(n\),仍然不够优秀。

考虑树状数组式的构造的本质:是二进制拆解,按照 \(lowbit\) 从小到大枚举。尝试换成 \(B\) 进制?

仍然按照 \(lowbit\) 从小到大枚举,每次只将 \(lowbit\) 那一位加 \(1\)
不断调整 \(B\),发现 \(B=63\) 时可以通过。

由于 \(62^3=238328,63^3=250047\),因此选取 \(B=63\) 时,最大的 \(lowbit\)\(63^2=3969\),而选取 \(B=62\),最大的 \(lowbit\)\(62^3=238328\)。在调整参数时可以优先考虑开若干次根号的临界值。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define i128 __int128
#define vi vector<int>
#define pii pair<int, int>
#define siz(a) ((int)a.size())
// #define fi first
// #define se second
// #define double long double
// #define int ll

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

void get(int n, int K)
{
	vector<int> a[100][105];
	for(int i = 1; i < n; ++i)
	{
		int t = 1, x = i, cnt = 0;
		while(x % K == 0) x /= K, t *= K, ++cnt;
		a[cnt][x % K].emplace_back(i);
	}
	for(int i = 0; i <= 6; ++i)
	{
		for(int j = 1; j < K; ++j)
		{
			if(siz(a[i][j]) > 0)
			{
				reverse(a[i][j].begin(), a[i][j].end());
			}
		}
	}
	ll t = 1, ans = 0, temp = 0;
	for(int i = 0; i <= 6; ++i)
	{
		for(int j = 1; j < K; ++j)
		{
			for(auto x : a[i][j])
			{
				printf("%d %d\n", x, min(t, (ll)n - x));
				ans += min(t, (ll)n - x);
				++temp;
			}
			if(temp < n - 1)
			{
				ans += 1000;
			}
		}
		t = t * K;
	}
	// printf("ans = %lld\n", ans);
}

void solve()
{
	int n = read();
	ll ans = 0, B = 63;
	if(n == 4)
	{
		printf("4\n1 4\n3 1\n2 1\n3 1\n");
	}else
	{
		printf("%d\n", n - 1);
		get(n, B);
	}
}

signed main()
{
    int T = 1;
    while(T--) solve();
    return 0;
}

Problem - G - Codeforces

待补。

posted @ 2025-11-23 23:51  梨愁浅浅  阅读(23)  评论(0)    收藏  举报