A.Only Pluses (枚举)
题意:
给出三个整数 aaa 、 bbb 和 ccc 可以执行以下操作最多 555 次。
- 挑选其中一个整数;
- 将其增加 111 。
通过这些操作可以实现的 a×b×ca \times b \times ca×b×c 的最大值是多少?
分析:
枚举a,b,ca,b,ca,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 (模拟)
题意:
给出一个数组,每次操作可以将数组中的一个数xxx分成x−1x-1x−1和111,或者将一个数xxx和一个111合并,问最少多少次操作可以将数组中所有数合并成一个。
分析:
我们从小到大将除了最大值的每个数字xxx分成xxx个111,再将他们全都合并到最大的数字上,每个数字的贡献是2×x−12 \times x-12×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 (贪心)
题意:
给出三个数字 nnn 、 mmm 和 kkk。他们决定构造一个长度为 nnn 的排列。
对于该排列, g(i)g(i)g(i) 是长度为 iii 的前缀上排列中所有不大于 mmm 的数字之和。其中 f(i)f(i)f(i) 是长度为 iii 的前缀上排列中所有不小于 kkk 的数字之和。长度为 iii 的前缀是由原始数组的前 iii 个元素组成的子数组。
例如,如果 n=5n = 5n=5 、 m=2m = 2m=2 、 k=5k = 5k=5 ,且排列为 [5,3,4,1,2][5, 3, 4, 1, 2][5,3,4,1,2] ,则:
- f(1)=5f(1) = 5f(1)=5 ,因为 5≥55 \ge 55≥5 ; g(1)=0g(1) = 0g(1)=0 ,因为 5>25 > 25>2 ;
- f(2)=5f(2) = 5f(2)=5 ,因为 3<53 < 53<5 ; g(2)=0g(2) = 0g(2)=0 ,因为 3>23 > 23>2 ;
- f(3)=5f(3) = 5f(3)=5 ,因为 4<54 < 54<5 ; g(3)=0g(3) = 0g(3)=0 ,因为 4>24 > 24>2 ;
- f(4)=5f(4) = 5f(4)=5 ,因为 1<51 < 51<5 ; g(4)=1g(4) = 1g(4)=1 ,因为 1≤21 \le 21≤2 ;
- f(5)=5f(5) = 5f(5)=5 ,因为 2<52 < 52<5 ; g(5)=1+2=3g(5) = 1 + 2 = 3g(5)=1+2=3 ,因为 2≤22 \le 22≤2 。
帮助他们找到一个使 (∑i=1nf(i)−∑i=1ng(i))\left(\sum_{i=1}^n f(i) - \sum_{i=1}^n g(i)\right)(∑i=1nf(i)−∑i=1ng(i)) 的值最大化的排列。
分析:
我们贪心的考虑这个问题,最优解为:让不超过mmm的数尽量靠后,不小于kkk的数字尽量靠前进行构造即可。
代码:
#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)
题意:
ErnKorErnKorErnKor要过河,这条河有nnn个格子,LLL代表平台,可以跳往[L,L+m][L,L + m][L,L+m]的任意位置,WWW代表水,每经过一格水要消耗一个体力,最开始有kkk个体力,也就是最多经过kkk格水,CCC代表鳄鱼,不能待在CCC所在的格子。给定n,m,kn,m,kn,m,k和一个代表河流的字符串,问能否到达对岸。
分析:
dp[i]dp[i]dp[i]表示到达第iii个格子需要游多少米,如果当前是陆地,就枚举往后跳了jjj格,则有dp[i+j]=dp[j]dp[i+j]=dp[j]dp[i+j]=dp[j],如果当前是水域,有dp[i+1]=dp[i]+1dp[i+1]=dp[i]+1dp[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)
题意:
给一个数字nnn,求出100001000010000以内的所有对a,ba,ba,b,能够满足nnn复制aaa次构成的字符串去掉末尾的bbb个数字得到的结果和n×a−bn \times a-bn×a−b相等。
分析:
因为这个数字至多只有666位,我们可以枚举aaa,再从1~61~61~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 (贪心)
题意:
有nnn 张卡片,上面标有不同位置的价格,第 iii 张卡片上有一个整数 aia_iai ,这些价格中没有一个正整数 xxx 。
将这些卡片分成最少数量的坏段(以便每张卡片都只属于一个段)。如果无法选择乘积等于 xxx 的卡片子集,则该段被视为坏段。将卡片分成的所有段都必须是坏段。
正式来说,如果没有索引 i1<i2<…<iki_1 < i_2 < \ldots < i_ki1<i2<…<ik 满足 l≤i1,ik≤rl \le i_1, i_k \le rl≤i1,ik≤r 和 ai1⋅ai2…⋅aik=xa_{i_1} \cdot a_{i_2} \ldots \cdot a_{i_k} = xai1⋅ai2…⋅aik=x ,则段 (l,r)(l, r)(l,r) 为坏段。
请确定坏段的最小数量。
分析:
考虑到xxx的除数不超过100100100,因此考虑枚举。我们维护一个数组用于存放xxx的除数的数组。如果a[i]a[i]a[i]是xxx的除数,那么就与在该数组中的元素相乘(注意不要超过xxx,防止数据溢出),如果该数组中出现了xxx , 那么使答案加一并清空数组,然后把 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 (组合数)
题意:
给出一个长度为 nnn 的数组 aaa ,由数字 1,2,…,n1, 2, \ldots, n1,2,…,n 组成。
让 MEX(S,k)\text{MEX}(S, k)MEX(S,k) 为升序排列的第 kkk 个正(严格大于零)整数,该整数不存在于集合 SSS 中。将 MEOW(a)\text{MEOW}(a)MEOW(a) 表示为数组 aaa 的所有不同 子集 bbb 的 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) = 1MEX({3,2},1)=1 ,因为 111 是集合中不存在的第一个正整数;
- MEX({4,2,1},2)=5\text{MEX}(\{4,2,1\}, 2) = 5MEX({4,2,1},2)=5 ,因为集合中不存在的前两个正整数是 333 和 555 ;
- MEX({},4)=4\text{MEX}(\{\}, 4) = 4MEX({},4)=4 ,因为空集中没有数字,所以其中不存在的前 444 个正整数是 1,2,3,41, 2, 3, 41,2,3,4 。
分析:
我们考虑枚举集合的大小iii和最后的结果jjj,需要满足以下条件:
-
集合中不能含有jjj
-
[1,j−1][1,j-1][1,j−1]区间内的数字需要取(j−i−1)(j-i-1)(j−i−1)个。
-
如果集合大小还没满iii,就在[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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

178



被折叠的 条评论
为什么被折叠?



