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个. 图形不能进行旋转翻转等操作. 现在要将这些图形拼成一条, 问方案数.

1708702353474

思路

实际上第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;
}

链接

https://codeforces.com/contest/1931

posted @ 2024-02-23 23:45  NewOri  阅读(48)  评论(0)    收藏  举报