1931
A. Recovering a Small String
题目大意
字母\(a\sim z\)用数字\(1\sim 26\)来代替,对于一个小写字母字符串,其值为各个字母代表数字之和.
给定一个三字母单词的值\(n\),求字典序最小的单词.
思路
设答案为为\(c_1c_2c_3\)那么\(c_1=max(1,n-52),\ c_2=max(1,n-c_1-26),\ c_3=n-c_1-c_2\)
代码
#include <bits/stdc++.h>
using ll = long long;
#define endl '\n'
using namespace std;
void solve(){
int n;
cin >> n;
int a, b, c;
a = max(1, n - 52);
n -= a;
b = max(1, n - 26);
n -= b;
c = n;
cout << (char)(a + 'a' - 1) << (char)(b + 'a' - 1) << (char)(c + 'a' - 1) << endl;
}
int main() {
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}
B. Make Equal
题目大意
给定\(n\)个数\(a_1,cdots,a_n\),可以进行任意此以下操作:\(a_i -= x, a_j += x.(x > 0, i < j)\)
问是否能使得所有数相等,数据保证和是\(n\)的倍数
思路
最后每个数都会变成平均值avg,先计算平均值,然后从1遍历到n,用sum储存多余的值.在遍历过程中,
如果\(a[i] > sum\), 那么\(sum += a[i] - sum\)
如果\(a[i] < sum\),
- 若\(sum >= avg - a[i], sum -= avg - a[i]\)
- 否则输出NO
代码
#include <bits/stdc++.h>
using ll = long long;
#define endl '\n'
using namespace std;
void solve(){
int n;
cin >> n;
vector<int> v(n);
ll sum = 0;
for(auto &e : v){
cin >> e;
sum += e;
}
ll avg = sum / n;
sum = 0;
int flag = 1;
for(int i = 0; i < n; i++){
if(v[i] + sum < avg){
flag = 0;
break;
}
else if(v[i] > avg){
sum += v[i] - avg;
}
else if(v[i] < avg){
sum -= avg - v[i];
}
}
if(flag) cout << "YES" << endl;
else cout << "NO" << endl;
}
int main() {
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}
C. Make Equal Again
题目大意
给定一个长度为n的数组\(A=(a_1,cdots,a_n)\),可以至多一次下列操作:
选择\(i,j,x\),将\(a_i\sim a_j\)替换为x, 代价为\(j - i + 1\)(区间长度)
问将所有数都变为同一个数的最小代价
思路
先说结论, 最后的结果中, 要么全都等于\(a_1\), 要么全都等于\(a_n\).
证明: 考虑使得代价最小的区间为\([i, j]\), \(1\)和\(n\)不会同时在区间\([i, j]\)内, 因为总有更小的区间满足条件, 例如\([1, n - 1]\).
既然\(1\)和\(n\)不会同时在区间内, 说明\(a_1\)和\(a_n\)至多有一个会被改变, 不被改变的那个就是我们所需要的\(x\)
具体做法: 分别假定\(a_1, a_n\)为\(x\), 计算出相应的代价, 取最小值
代码
#include <bits/stdc++.h>
using ll = long long;
#define endl '\n'
using namespace std;
void solve(){
int n;
cin >> n;
vector<int> v(n);
int l1 = 0, r1 = n - 1, l2 = 0 ,r2 = n - 1;
for(int i = 0; i < n; i++){
cin >> v[i];
}
while(l1 < n && v[l1] == v[0]) l1++;
while(r1 >= 0 && v[r1] == v[0]) r1--;
while(l2 < n && v[l2] == v[n - 1]) l2++;
while(r2 >= 0 && v[r2] == v[n - 1]) r2--;
int ans = min(r1 - l1 + 1, r2 - l2 + 1);
ans = max(0, ans);//考虑全都相等的情况
cout << ans << endl;
}
int main() {
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}
D. Divisible Pairs
题目大意
给定\(n, x, y\), 和长度一个为n的数组a, 定义有序对\(<i, j>,(i < j)\)是美丽的当
- \(x \mid a_i + a_j\)
- \(y \mid a_i - a_j\)
问数组a中美丽的有序对的数量.
思路
考虑第\(j\)个数\(a_j\), 我们需要统计有多少个\(i(i < j)\)满足 \(a_i \equiv x - a_j\ (mod\ x),\, a_i \equiv a_j\ (mod\ y)\).如果直接暴力, 复杂度是\(O(n^2)\)的.
考虑用map存储有序对\((a_i \% x, a_i \%y)的数量\). 要知道有多少个i可以和j组成美丽的有序对, 只需要查询有序对\(((x - a_j) \% x, a_j \% y)\)的数量. 复杂度\(O(nlogn)\).
代码
#include <bits/stdc++.h>
using ll = long long;
#define endl '\n'
using namespace std;
using pii = pair<int, int>;
void solve(){
int x, y, n;
cin >> n>> x >> y;
vector<int> v(n);
for(auto &e : v) cin >> e;
map<pii, int> mp;
ll ans = 0;
for(int i = 0; i < n; i++){
pii p = pii((x - v[i] % x) % x, v[i] % y);
if(mp.count(p)){
ans += mp[p];
}
p = pii(v[i] % x, v[i] % y);
if(mp.count(p)){
mp[p]++;
}
else mp[p] = 1;
// cout << v[i] % x << " " << v[i] % y << endl;
}
cout << ans << endl;
}
int main() {
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}
E. Anna and the Valentine's Day Gift
题目大意
Anna和Sasha正在玩一个游戏, Anna先手, 轮流操作, 游戏的规则如下:
给定一个长度为\(n\)的数组\(a\), 和一个正整数m. Anna可以选择\(a\)中一个数将其数位翻转并在翻转后删去前导0, Sasha可以拿出两个数并将他们以任意前后顺序拼在一起随后放回数组.
Anna希望最后得到的数字小于\(10^m\), Sasha希望它大于等于\(10^m\). 问最后谁能获胜
思路
Anna希望小于\(10^m\), 也就是最后数字的位数小于等于\(m\). Anna的最优策略是每次都选择后导0数量最多的数进行翻转. 而Sasha的最优策略是把后导0数量最多的数字和随便哪个数字拼起来, 使得Anna无法把这个数的后导0删去.
最终做法是按照后导0数量排序, 计算最后数字的位数是否小于等于m.
代码
#include <bits/stdc++.h>
using ll = long long;
#define endl '\n'
using namespace std;
void solve(){
int n, m;
cin >> n >> m;
vector<int> a(n);
vector<int> zero(n, 0);//后缀0的数量
vector<int> w(n, 0);//位数
vector<int> idx(n);
for(int i = 0; i < n; i++){
cin >> a[i];
int t = a[i];
while(t > 0 && t % 10 == 0){
w[i]++;
zero[i]++;
t /= 10;
}
while(t > 0){
w[i]++;
t /= 10;
}
idx[i] = i;
}
sort(idx.begin(), idx.end(), [&zero](int a, int b){return zero[a] > zero[b];});
int sum = 0;
for(int i = 0; i < n ; i++){
if(i % 2 == 0){
sum -= zero[idx[i]];
}
sum += w[idx[i]];
}
if(sum < m + 1) cout << "Anna" << endl;
else cout << "Sasha" << endl;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}
F. Chat Screenshots
题目大意
有n个人, 对于一个长度为n的排列, 每个人的screenshot仅会将自己的位置提到最前面.
现在给出k个人的screenshot, 问是否可能存在一个排列, 符合这k个人的screenshot
官方思路
每一行从第2个人到第n个人的顺序我们是知道的, 那么我们可以根据这个建立一个有向图.
将k * (n - 2)条边加入到有向图中, 如果没有环就说明有解.
我的思路
尝试找出原序列.
首先判断每一行开头是否有重复出现的数字.
- 当 k > 2时
- 如果每一行的末尾都相同, 且\(k < n\), 那么将每行末尾删去并更新末尾.
- 如果每一行的末尾有1个\(a\), \(k - 1\)个\(b\), 考察\(a\)那行的头部是否是\(b\), 如果不是则无解
- 每一行最多有一个数被提到前面
- 每个数最多被提到前面一次
- 及时更新头部和末尾的值
- 当 k = 2时, 一些特殊情况导致无法使用k > 2时的方法判断, 因此特判.
- 设第一行开头数字是c1, 第二行开头数字是c2. 同时遍历第一行和第二行, 如果遇到c1或c2就跳过, 判断对应数字是否相等.
- k = 1肯定可以
虽然我这个方法写的代码过了, 但是十分的丑陋. 写的时候脑子属于是瓦特了, 没有想到正解的方法. 而且我感觉我这个方法直觉上感觉是对的, 但同时直觉上感觉不那么对. 这里就不贴代码了.
G. One-Dimensional Puzzle
题目大意
有如下四种图形, 分别有c1, c2, c3, c4个. 图形不能进行旋转翻转等操作. 现在要将这些图形拼成一条, 问方案数.

思路
实际上第3种和第4种图形是"没用的", 在正确的图形序列中删去他们后还是一整条. 例如132删去后变为12. 所有正确的图形序列的可以看作图形1和图形2拼好了然后插入图形3和图形4.
3可以插入任意个到12之间, 4可以插入任意个到21之间. 讨论3种情况,
- \(c1 - c2 = -1\)
- \(c1 - c2 = 0\)
- \(c1 - c2 = 1\)
用隔板法计算方案数.
代码
#include <bits/stdc++.h>
using ll = long long;
#define endl '\n'
using namespace std;
const ll mod = 998244353;
ll qpow(ll a, ll n){
if(n == 0) return 1;
ll res = qpow(a, n / 2);
res *= res; res %= mod;
if(n & 1) res *= a;
return res % mod;
}
ll C(ll n, ll m){
if(m > n - m) m = n - m;
ll res = 1;
for(int i = 1; i <= m; i++){
res *= (n - i + 1);
res %= mod;
res *= qpow(i, mod - 2);
res %= mod;
}
return res;
}
void solve(){
ll c1, c2, c3, c4;
cin >> c1 >> c2 >> c3 >> c4;
ll ans = 0;
if(c1 == 0 && c2 == 0){
if(c3 * c4 == 0) ans = 1;
}
else if(c1 == 0 && c2 == 1){
ans = 1;
}
else if(c2 == 0 && c1 == 1){
ans = 1;
}
else if(c1 - c2 == 1){
ans = C(c1 + c3 - 1, c1 - 1) * C(c2 + c4, c2);
}
else if(c1 - c2 == -1){
ans = C(c1 + c3 , c1) * C(c2 + c4 - 1, c2 - 1);
}
else if(c1 == c2){
ans = C(c1 + c3 - 1, c1 - 1) * C(c2 + c4, c2) + C(c1 + c3, c1) * C(c2 + c4 - 1, c2 - 1);
}
cout << ans % mod << endl;
}
int main() {
int t = 1;
cin >> t;
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号