Educational Codeforces Round 86 个人题解
1342A - Road To Zero(简单分类讨论)
题意:给两个整数 x x x 和 y y y,需要把这两个数都变成 0 0 0,现在有 a a a 和 b b b 两个操作,其中 a a a 操作花费 a a a 的代价将其中一个数字 + 1 +1 +1 或者 − 1 -1 −1, b b b 操作花费 b b b 的代价将两个数字同时 + 1 +1 +1 或者 − 1 -1 −1,问最小的代价是多少,需要回答 t t t 个案例。
范围: 1 ≤ t ≤ 100 , 0 ≤ x , y ≤ 1 e 9 , 1 ≤ a , b ≤ 1 e 9 1 \le t \le 100, 0 \le x, y \le 1e9, 1 \le a, b \le 1e9 1≤t≤100,0≤x,y≤1e9,1≤a,b≤1e9
题解:
显然对 x x x 和 y y y 两个数字都进行一次 a a a 操作,其效果等价于进行了一次 b b b 操作,如果 2 ∗ a < b 2*a < b 2∗a<b 则 b b b 操作完全可以被 a a a 操作取代,否则就需要进行 ∣ x − y ∣ |x-y| ∣x−y∣ 次 a a a 操作消除两数的差异再进行 m i n ( x , y ) min(x, y) min(x,y) 次 b b b 操作。
Code:
#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
const int MAXN = 2e5;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
int n;
signed main()
{
int T = read();
while (T--)
{
int x, y, a, b;
cin >> x >> y >> a >> b;
if (x > y)
swap(x, y);
if (2 * a <= b)
{
cout << a * (x + y) << endl;
}
else
{
cout << b * x + a * (y - x) << endl;
}
}
return 0;
}
1342B - Binary Period(构造)
题意:给一个 01 01 01 串 t t t,需要找到一个长度不超过 2 ∗ ∣ t ∣ 2*|t| 2∗∣t∣ 的 01 01 01 串 s s s 满足: t t t 是 s s s 的子序列且 s s s 拥有最小的循环长度。只需要给出一个满足条件的 s s s ,需要回答 t t t 个案例。
范围: 1 ≤ T ≤ 100 , 1 ≤ ∣ t ∣ ≤ 100 1 \le T \le 100, 1 \le |t| \le 100 1≤T≤100,1≤∣t∣≤100
题解:
不要求最优解,考虑找一下规律。可以发现如果 t t t 中只包含 1 1 1 或者只包含 0 0 0,那么 s s s 也只需要包含 1 1 1 或者只包含 0 0 0,循环长度为 1 1 1;如果 t t t 中既有 0 0 0 也有 1 1 1,那么 s s s 可以构造成以 " 01 " "01" "01" 为最小循环子串的长度为 2 ∗ ∣ t ∣ 2*|t| 2∗∣t∣ 的 01 01 01 串,循环长度为 2 2 2,这样对于 t t t 中的每一位都可以在一个 " 01 " "01" "01" 循环子串中匹配到。
Code:
#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
const int MAXN = 2e5;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
int n;
signed main()
{
int T = read();
while (T--)
{
string str;
cin >> str;
int cnt1 = 0, cnt2 = 0;
for (int i = 0; i < str.length(); i++)
{
if (str[i] == '0')
cnt1++;
else
cnt2++;
}
string ans = "";
// 只有1
if (cnt1 == 0)
{
for (int i = 0; i < 2 * str.length(); i++)
{
ans += '1';
}
}
// 只有0
else if (cnt2 == 0)
{
for (int i = 0; i < 2 * str.length(); i++)
{
ans += '0';
}
}
// 两个都有
else
{
for (int i = 0; i < str.length(); i++)
{
ans += '0';
ans += '1';
}
}
cout << ans << endl;
}
return 0;
}
1342C - Yet Another Counting Problem(数学+前缀和)
题意:有 t t t 个案例,每个案例给两个整数 a a a 和 b b b,以及 q q q 个询问,每个询问给出 l l l 和 r r r,需要计算在区间 [ l , r ] [l, r] [l,r] 中有多少个数字 x x x 满足 x % a % b = x % b % a x\%a\%b = x\%b\%a x%a%b=x%b%a。
范围: 1 ≤ t ≤ 100 , 1 ≤ a , b ≤ 200 , 1 ≤ q ≤ 500 , 1 ≤ l ≤ r ≤ 1 e 18 1 \le t \le 100, 1 \le a, b \le 200, 1 \le q \le 500, 1 \le l \le r \le 1e18 1≤t≤100,1≤a,b≤200,1≤q≤500,1≤l≤r≤1e18
题解:
可以发现对于给定的 a a a 和 b b b,从小到大满足条件的 x x x 以一个周期进行循环,而这个周期就是 a a a 和 b b b 的公倍数,每个周期内满足条件的 x x x 数量相同。显然 a ∗ b a*b a∗b 是 a a a 和 b b b 的公倍数,且 a ∗ b ≤ 4 e 4 a*b \le 4e4 a∗b≤4e4,那么我们就可以预处理出一个周期内满足条件 x x x 的前缀和,因此就可以对所有的查询区间 [ l , r ] [l, r] [l,r] 进行前缀和差分求解, a n s = p r e [ y ] − p r e [ x − 1 ] ans = pre[y]-pre[x-1] ans=pre[y]−pre[x−1]。
Code:
#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
const int MAXN = 4e4 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
int a, b, q;
int dp[MAXN];
int calc(int x)
{
// 有x/(a*b)个完整周期,还剩下一个x%(a*b)的周期
return dp[a * b] * (x / (a * b)) + dp[x % (a * b)];
}
signed main()
{
int T = read();
while (T--)
{
memset(dp, 0, sizeof(dp));
cin >> a >> b >> q;
// 预处理前缀和
dp[0] = 0;
for (int i = 1; i <= a * b; i++)
{
if (i % a % b == i % b % a)
dp[i] = dp[i - 1];
else
dp[i] = dp[i - 1] + 1;
}
for (int i = 0; i < q; i++)
{
int x, y;
cin >> x >> y;
// 前缀差分
cout << calc(y) - calc(x - 1) << endl;
}
}
return 0;
}
1342D - Multiple Testcases(二分)
题意:有 n n n 个数字 m 1 ∼ m n m_1\sim m_n m1∼mn,并且有一种容器,一个容器只能存放 c 1 c_1 c1 个 ≥ 1 \ge1 ≥1 的数字, c 2 c_2 c2 个 ≥ 2 \ge2 ≥2 的数字,…, c k c_k ck 个 ≥ k \ge k ≥k 的数字,问至少需要多少个容器才能装下所有数字。
范围: 1 ≤ n , k ≤ 2 e 5 , 1 ≤ m i ≤ k , n ≥ c 1 ≥ c 2 ≥ . . . ≥ c k ≥ 1 1\le n, k \le 2e5, 1 \le m_i \le k, n \ge c_1 \ge c_2 \ge ... \ge c_k \ge 1 1≤n,k≤2e5,1≤mi≤k,n≥c1≥c2≥...≥ck≥1
题解:
考虑到数据范围,我们不能够对每个容器的各个档位去找数字存放,也不能对于每个数字直接遍历去找哪个容器还可以存放,不过可以利用二分进行优化遍历的过程。
首先我们对数字进行排序,从大到小的进行处理,对于每个数字需要找到哪个容器可以容纳下该数字,而进行遍历的效率太低。
基于贪心的想法,一个容器需要尽可能多的存放数字,实在没办法了才使用新的容器,那么最后每个容器中的数字数量会呈现递减的形式,对于这样的一个序列我们可以使用二分进行快速查找。
由于最多只会使用 2 e 5 2e5 2e5 个容器,那么我们可以建立一个数组 n u m num num 保存每个容器中数字的数量。对于当前需要插入的数字 x x x,进行二分找到第一个 n u m < c [ x ] num < c[x] num<c[x] 的位置进行插入,该容器至少还剩下 1 1 1 个位置可以存放 ≥ x \ge x ≥x 的数字,将 x x x 加入该容器中即可。
详见代码。
Code:
#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
const int MAXN = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const double PI = acos(-1.0);
int n, k;
int m[MAXN], cap[MAXN], num[MAXN];
vector<int> vec[MAXN];
signed main()
{
n = read(), k = read();
for (int i = 0; i < n; i++)
{
int x = read();
m[x]++; // m[x]表示数字x的数量
}
for (int i = 1; i <= k; i++)
{
cap[i] = read();
}
// maxV用来保存容器的数量
int maxV = 0;
// 从后往前遍历
for (int i = k; i >= 1; i--)
{
// 处理m[i]个数字i
while (m[i])
{
// 二分找到第一个num<c[i]的容器
int l = 0, r = MAXN;
int ans;
while (l <= r)
{
int mid = l + r >> 1;
if (num[mid] < cap[i])
{
ans = mid;
r = mid - 1;
}
else
{
l = mid + 1;
}
}
// 更新容器数量
maxV = max(maxV, ans);
// 更新该容器大小
num[ans]++;
// 减少一个数字i
m[i]--;
// 将数字i加入容器ans中
vec[ans].push_back(i);
}
}
// 输出答案
cout << maxV + 1 << endl;
for (int i = 0; i <= maxV; i++)
{
cout << vec[i].size();
for (auto x : vec[i])
{
cout << " " << x;
}
cout << endl;
}
return 0;
}
【END】感谢观看!