A.Only Pluses (枚举)
题意:
给出三个整数 a a a 、 b b b 和 c c c 可以执行以下操作最多 5 5 5 次。
- 挑选其中一个整数;
- 将其增加 1 1 1 。
通过这些操作可以实现的 a × b × c a \times b \times c a×b×c 的最大值是多少?
分析:
枚举 a , b , c a,b,c a,b,c三个数在操作之后的值即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 805;
const int INF = 1e9;
const int mod = 998244353;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)
{
int a, b, c;
cin >> a >> b >> c;
int ans = a * b * c;
for (int i = a; i <= a + 5; i++)
{
for (int j = b; j <= b + 5; j++)
{
for (int k = c; k <= c + 5; k++)
{
if (i + j + k - (a + b + c) <= 5)
ans = max(ans, i * j * k);
}
}
}
cout << ans << endl;
}
return 0;
}
B.Angry Monk (模拟)
题意:
给出一个数组,每次操作可以将数组中的一个数 x x x分成 x − 1 x-1 x−1和 1 1 1,或者将一个数 x x x和一个 1 1 1合并,问最少多少次操作可以将数组中所有数合并成一个。
分析:
我们从小到大将除了最大值的每个数字 x x x分成 x x x个 1 1 1,再将他们全都合并到最大的数字上,每个数字的贡献是 2 × x − 1 2 \times x-1 2×x−1。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 805;
const int INF = 1e9;
const int mod = 998244353;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)
{
int n, k, mx = 0;
cin >> n >> k;
int ans = 0;
for (int i = 1; i <= k; i++)
{
int tmp;
cin >> tmp;
mx = max(mx, tmp);
ans += 2 * tmp - 1;
}
cout << ans - 2 * mx + 1 << endl;
}
return 0;
}
C. Gorilla and Permutation (贪心)
题意:
给出三个数字 n n n 、 m m m 和 k k k。他们决定构造一个长度为 n n n 的排列。
对于该排列, g ( i ) g(i) g(i) 是长度为 i i i 的前缀上排列中所有不大于 m m m 的数字之和。其中 f ( i ) f(i) f(i) 是长度为 i i i 的前缀上排列中所有不小于 k k k 的数字之和。长度为 i i i 的前缀是由原始数组的前 i i i 个元素组成的子数组。
例如,如果 n = 5 n = 5 n=5 、 m = 2 m = 2 m=2 、 k = 5 k = 5 k=5 ,且排列为 [ 5 , 3 , 4 , 1 , 2 ] [5, 3, 4, 1, 2] [5,3,4,1,2] ,则:
- f ( 1 ) = 5 f(1) = 5 f(1)=5 ,因为 5 ≥ 5 5 \ge 5 5≥5 ; g ( 1 ) = 0 g(1) = 0 g(1)=0 ,因为 5 > 2 5 > 2 5>2 ;
- f ( 2 ) = 5 f(2) = 5 f(2)=5 ,因为 3 < 5 3 < 5 3<5 ; g ( 2 ) = 0 g(2) = 0 g(2)=0 ,因为 3 > 2 3 > 2 3>2 ;
- f ( 3 ) = 5 f(3) = 5 f(3)=5 ,因为 4 < 5 4 < 5 4<5 ; g ( 3 ) = 0 g(3) = 0 g(3)=0 ,因为 4 > 2 4 > 2 4>2 ;
- f ( 4 ) = 5 f(4) = 5 f(4)=5 ,因为 1 < 5 1 < 5 1<5 ; g ( 4 ) = 1 g(4) = 1 g(4)=1 ,因为 1 ≤ 2 1 \le 2 1≤2 ;
- f ( 5 ) = 5 f(5) = 5 f(5)=5 ,因为 2 < 5 2 < 5 2<5 ; g ( 5 ) = 1 + 2 = 3 g(5) = 1 + 2 = 3 g(5)=1+2=3 ,因为 2 ≤ 2 2 \le 2 2≤2 。
帮助他们找到一个使 ( ∑ i = 1 n f ( i ) − ∑ i = 1 n g ( i ) ) \left(\sum_{i=1}^n f(i) - \sum_{i=1}^n g(i)\right) (∑i=1nf(i)−∑i=1ng(i)) 的值最大化的排列。
分析:
我们贪心的考虑这个问题,最优解为:让不超过 m m m的数尽量靠后,不小于 k k k的数字尽量靠前进行构造即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 3e5 + 5;
const int INF = 1e9;
const int mod = 998244353;
int a[maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)
{
int n, m, k;
cin >> n >> m >> k;
int l, r;
int x = 1;
for (int i = n - m + 1; i <= n; i++)
{
a[i] = x;
x++;
}
x = n;
for (int i = 1; i <= n - m; i++)
{
a[i] = x;
x--;
}
for (int i = 1; i <= n; i++)
{
cout << a[i] << ' ';
}
cout << endl;
}
return 0;
}
D. Test of Love(dp)
题意:
E r n K o r ErnKor ErnKor要过河,这条河有 n n n个格子, L L L代表平台,可以跳往 [ L , L + m ] [L,L + m] [L,L+m]的任意位置, W W W代表水,每经过一格水要消耗一个体力,最开始有 k k k个体力,也就是最多经过 k k k格水, C C C代表鳄鱼,不能待在 C C C所在的格子。给定 n , m , k n,m,k n,m,k和一个代表河流的字符串,问能否到达对岸。
分析:
d p [ i ] dp[i] dp[i]表示到达第 i i i个格子需要游多少米,如果当前是陆地,就枚举往后跳了 j j j格,则有 d p [ i + j ] = d p [ j ] dp[i+j]=dp[j] dp[i+j]=dp[j],如果当前是水域,有 d p [ i + 1 ] = d p [ i ] + 1 dp[i+1]=dp[i]+1 dp[i+1]=dp[i]+1。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 3e5 + 5;
const int INF = 1e9;
const int mod = 998244353;
int a[maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)
{
int n, m, k;
cin >> n >> m >> k;
string s;
cin >> s;
s = "L" + s + "L";
vector<int> dp(n + 2, 1e9);
dp[0] = 0;
for (int i = 0; i <= n; i++)
{
if (dp[i] > k or s[i] == 'C')
continue;
if (s[i] == 'L')
{
for (int j = 1; j <= m and i + j <= n + 1; j++)
{
dp[i + j] = min(dp[i + j], dp[i]);
}
}
else
{
dp[i + 1] = min(dp[i + 1], dp[i] + 1);
}
}
if (dp[n + 1] <= k)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
E. Novice’s Mistake (dp)
题意:
给一个数字 n n n,求出 10000 10000 10000以内的所有对 a , b a,b a,b,能够满足 n n n复制 a a a次构成的字符串去掉末尾的 b b b个数字得到的结果和 n × a − b n \times a-b n×a−b相等。
分析:
因为这个数字至多只有 6 6 6位,我们可以枚举 a a a,再从 1 ~ 6 1~6 1~6枚举这个数字的位数,判断由字符串得到的数字是否满足条件。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 3e5 + 5;
const int INF = 1e9;
const int mod = 998244353;
vector<PII> ans[110];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
string s = to_string(n);
int len = s.size();
s += s, s += s, s += s, s += s;
for (int a = 1; a <= 1e4; a++)
{
int num = 0;
for (int j = 1; j <= 6; j++)
{
num *= 10;
num += s[j - 1] - 48;
int b = a * len - j;
if (b <= 0 || b >= 1e4)
continue;
if (n * a - b == num)
{
PII p;
p.first = a, p.second = b;
ans[n].push_back(p);
}
}
}
cout << ans[n].size() << endl;
for (int i = 0; i < ans[n].size(); i++)
{
cout << ans[n][i].first << ' ' << ans[n][i].second << endl;
}
}
return 0;
}
F. Valuable Cards (贪心)
题意:
有 n n n 张卡片,上面标有不同位置的价格,第 i i i 张卡片上有一个整数 a i a_i ai ,这些价格中没有一个正整数 x x x 。
将这些卡片分成最少数量的坏段(以便每张卡片都只属于一个段)。如果无法选择乘积等于 x x x 的卡片子集,则该段被视为坏段。将卡片分成的所有段都必须是坏段。
正式来说,如果没有索引 i 1 < i 2 < … < i k i_1 < i_2 < \ldots < i_k i1<i2<…<ik 满足 l ≤ i 1 , i k ≤ r l \le i_1, i_k \le r l≤i1,ik≤r 和 a i 1 ⋅ a i 2 … ⋅ a i k = x a_{i_1} \cdot a_{i_2} \ldots \cdot a_{i_k} = x ai1⋅ai2…⋅aik=x ,则段 ( l , r ) (l, r) (l,r) 为坏段。
请确定坏段的最小数量。
分析:
考虑到 x x x的除数不超过 100 100 100,因此考虑枚举。我们维护一个数组用于存放 x x x的除数的数组。如果 a [ i ] a[i] a[i]是 x x x的除数,那么就与在该数组中的元素相乘(注意不要超过 x x x,防止数据溢出),如果该数组中出现了 x x x , 那么使答案加一并清空数组,然后把 a [ i ] a[i] a[i] 加入到这个数组中,重复以上操作即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 3e5 + 5;
const int INF = 1e9;
const int mod = 998244353;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--)
{
int n, x;
cin >> n >> x;
int ans = 0;
vector<int> a(n + 1, 0);
for (int i = 1; i <= n; i++)
cin >> a[i];
map<LL, LL> f;
for (int i = 1; i <= n; i++)
{
if (x % a[i] == 0)
{
if (f.empty())
{
f[a[i]]++;
}
else
{
map<LL, LL> g;
for (auto y : f)
{
if (x % (y.first * a[i]) == 0)
g[y.first * a[i]]++;
}
for (auto y : g)
{
f[y.first] += y.second;
}
if (f.count(x))
{
ans++;
f.clear();
}
f[a[i]]++;
}
}
}
ans++;
cout << ans << endl;
}
return 0;
}
G. Ultra-Meow (组合数)
题意:
给出一个长度为 n n n 的数组 a a a ,由数字 1 , 2 , … , n 1, 2, \ldots, n 1,2,…,n 组成。
让 MEX ( S , k ) \text{MEX}(S, k) MEX(S,k) 为升序排列的第 k k k 个正(严格大于零)整数,该整数不存在于集合 S S S 中。将 MEOW ( a ) \text{MEOW}(a) MEOW(a) 表示为数组 a a a 的所有不同 子集 b b b 的 MEX ( b , ∣ b ∣ + 1 ) \text{MEX}(b, |b| + 1) MEX(b,∣b∣+1) 的总和。
集合的 MEX ( S , k ) \text{MEX}(S, k) MEX(S,k) 值示例:
- MEX ( { 3 , 2 } , 1 ) = 1 \text{MEX}(\{3,2\}, 1) = 1 MEX({3,2},1)=1 ,因为 1 1 1 是集合中不存在的第一个正整数;
- MEX ( { 4 , 2 , 1 } , 2 ) = 5 \text{MEX}(\{4,2,1\}, 2) = 5 MEX({4,2,1},2)=5 ,因为集合中不存在的前两个正整数是 3 3 3 和 5 5 5 ;
- MEX ( { } , 4 ) = 4 \text{MEX}(\{\}, 4) = 4 MEX({},4)=4 ,因为空集中没有数字,所以其中不存在的前 4 4 4 个正整数是 1 , 2 , 3 , 4 1, 2, 3, 4 1,2,3,4 。
分析:
我们考虑枚举集合的大小 i i i和最后的结果 j j j,需要满足以下条件:
-
集合中不能含有 j j j
-
[ 1 , j − 1 ] [1,j-1] [1,j−1]区间内的数字需要取 ( j − i − 1 ) (j-i-1) (j−i−1)个。
-
如果集合大小还没满 i i i,就在 [ j + 1 , n ] [j+1,n] [j+1,n]区间内取剩下的数字。
我们首先预处理出组合数,就可以进行计算。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int INF = 1e9;
const int mod = 1e9 + 7;
template <const int T>
struct ModInt
{
const static int mod = T;
int x;
ModInt(int x = 0) : x(x % mod) {}
ModInt(long long x) : x(int(x % mod)) {}
int val() { return x; }
ModInt operator+(const ModInt &a) const
{
int x0 = x + a.x;
return ModInt(x0 < mod ? x0 : x0 - mod);
}
ModInt operator-(const ModInt &a) const
{
int x0 = x - a.x;
return ModInt(x0 < 0 ? x0 + mod : x0);
}
ModInt operator*(const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
ModInt operator/(const ModInt &a) const { return *this * a.inv(); }
bool operator==(const ModInt &a) const { return x == a.x; };
bool operator!=(const ModInt &a) const { return x != a.x; };
void operator+=(const ModInt &a)
{
x += a.x;
if (x >= mod)
x -= mod;
}
void operator-=(const ModInt &a)
{
x -= a.x;
if (x < 0)
x += mod;
}
void operator*=(const ModInt &a) { x = 1LL * x * a.x % mod; }
void operator/=(const ModInt &a) { *this = *this / a; }
friend ModInt operator+(int y, const ModInt &a)
{
int x0 = y + a.x;
return ModInt(x0 < mod ? x0 : x0 - mod);
}
friend ModInt operator-(int y, const ModInt &a)
{
int x0 = y - a.x;
return ModInt(x0 < 0 ? x0 + mod : x0);
}
friend ModInt operator*(int y, const ModInt &a) { return ModInt(1LL * y * a.x % mod); }
friend ModInt operator/(int y, const ModInt &a) { return ModInt(y) / a; }
friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x; }
friend istream &operator>>(istream &is, ModInt &t) { return is >> t.x; }
ModInt pow(int64_t n) const
{
ModInt res(1), mul(x);
while (n)
{
if (n & 1)
res *= mul;
mul *= mul;
n >>= 1;
}
return res;
}
ModInt inv() const
{
int a = x, b = mod, u = 1, v = 0;
while (b)
{
int t = a / b;
a -= t * b;
swap(a, b);
u -= t * v;
swap(u, v);
}
if (u < 0)
u += mod;
return u;
}
};
using mint = ModInt<1000000007>;
const int maxn = 5005;
mint C[maxn][maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
cin >> T;
for (int i = 0; i <= 5000; i++)
{
for (int j = 0; j <= i; j++)
{
if (!j)
{
C[i][j] = 1;
}
else
{
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}
}
}
while (T--)
{
int n;
cin >> n;
mint ans = 1;
for (int i = 1; i <= n; i++)
{
for (int j = i + 1; j <= 2 * i + 1; j++)
{
int cnt = j - i - 1;
mint t = j * C[min(j - 1, n)][cnt];
if (n - j < 0)
{
if (cnt != i)
{
t = 0;
}
}
else
{
assert(n - j >= 0 and i - cnt >= 0);
t *= C[n - j][i - cnt];
}
ans += t;
}
}
cout << ans << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。