【ABC 313】A~E题解

好久没打过比赛了,也好久没写过题解。cf时间有点阴间,来做下ABC

这次做出了A~D,rk900+,E感觉赛时过的和D人数差不多,但我不是很会数数(哭

A

A - To Be Saikyo

题意:给你 \(n\) 个数 \(P_i\),找到最小的非负整数 \(x\) 使得 \(P_1+x>P_i\)

做法:$$x = max(x, P_i - P_1 + 1)$$ 循环一遍即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100 + 10, M = 2e6 + 10, mod = 100003;
int n, a[N];

int main(){
    ios::sync_with_stdio(false);
    cin >> n;
    int x = 0;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        if(i > 1) {
            x = max(x, a[i] - a[1] + 1);
        }
    }
    cout << x << endl;
	return 0;
} 

B

B - Who is Saikyo?

题意:有 \(n(n <= 50)\) 个人,\(m\) 对关系,每对关系 \((x,y)\) 表示 \(x\) 能力强于 \(y\),这种关系具有传递性。问谁是最强的,如果不能唯一确定,输出-1

做法:

  1. 有点复杂,我的做法是floyd,求出每两个人之间的关系
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100 + 10, M = 2e6 + 10, mod = 100003;
int n, m, a[N];
int g[N][N];

int floyd() {
    for(int k = 1; k <= n; k++) {
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                if(g[i][k] && g[k][j]) {
                    g[i][j] = 1;
                }
            }
        }
    }
    // for(int i = 1; i <= n; i++) {
    //     for(int j = 1; j <= n; j++) {
    //         if(i == j)continue;
    //         if(g[i][j] && g[j][i]) return -1;
    //     }
    // }
    for(int k = 1; k <= n; k++) {
        bool f = 1;
        for(int i = 1; i <= n; i++) {
            if(g[k][i] || i == k) continue;
            else {f = 0; break;}
        }
        if(f) return k; 
    }
    return -1;
}

int main(){
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i = 1, x, y; i <= m; i++) {
        cin >> x >> y;
        g[x][y] = 1;
    }
    cout << floyd() << endl;
	return 0;
} 

  1. \(x\) 强于 \(y\) 抽象成 \(x\)\(y\) 有一条有向边,这样可以知道,最强的人就是度数为0的点,假设这样的点只有一个,那么就是答案
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100 + 10, M = 2e6 + 10, mod = 100003;
int n, m, a[N];
int rd[N];

int main(){
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i = 1, x, y; i <= m; i++) {
        cin >> x >> y;
        rd[y]++;
    }
    int cnt = 0, ans;
    for(int i = 1; i <= n; i++) {
        if(!rd[i]) cnt++, ans = i;
    }
    if(cnt != 1) ans = -1;
    cout << ans << endl;
	return 0;
} 

C

C - Approximate Equalization 2

题意:给你 \(n(n <= 2*10^5)\) 个数,定义一次操作为一个数+=1,一个数-=1,问你最少需要多少次操作,能把整个数列变成最大值与最小值的差不超过1

做法:可以求出 $ave = \frac{\sum{a_i}}{n} $,改变后值为 \(ave + 1\) 的有 \(more = \sum{a_i} - ave * n\) 个,值为 \(ave\) 的有 \(n - more\) 个。将 \(a_i\) 排序,最后肯定是前 \(n - more\) 个值为 \(ave\), 后 \(more\) 个值为 \(ave + 1\)。因为序列增加的值的和与减小的值的和相等,让我们只看应该增大值的 \(a_i\),如果 \(a_i\) 小于它应该成为的值,就把 \(supposed(a_i) - a_i\) 加进答案。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 10;
ll n, m, a[N];

int main(){
    ios::sync_with_stdio(false);
    cin >> n;
    ll tot = 0;
    for(int i = 1, x, y; i <= n; i++) {
        cin >> a[i];
        tot += a[i];
    }
    sort(a + 1, a + n + 1);
    ll ave = tot / n;
    ll ans = 0, more = tot - ave * n;
    if(more == 0) {
        for(int i = 1, x, y; i <= n; i++) {
            if(a[i] < ave) ans += ave - a[i];
        }
    }
    else {
        // more 个 ave+1, n-more 个 ave
        for(int i = 1; i <= n - more; i++) {
            if(a[i] < ave) ans += ave - a[i];
            else break;
        }
        for(int i = n - more + 1; i <= n; i++) {
            if(a[i] < ave + 1) ans += ave + 1 - a[i];
        }
    }
    cout << ans << endl;
	return 0;
} 

D

D - Odd or Even

题意:交互题。有 \(n(n <= 1000)\) 个数,它们的值是0或者1,每次可以询问 \(k(k < n, k为奇数)\) 个数的和是奇数还是偶数, 最多问 \(n\) 次。求这 \(n\) 个数的值。

做法:
首先问 base = 【1,2,3,...,k-1,k】
然后依次问 tem =【1,2,3,...,k-1,i】 ... (i的值从k+1取到n),共 \(n - k + 1\) 次。可以知道k+1 到 n 这些数和 \(a_k\) 的值相同或不同。

之后,问【1,2,3,...,k+1,k】、【1,2,3,...,k+1,k-1,k】...(把k+1插入k-1、k-2、... 1的位置,其他数和base保持一致),共 \(k-1\) 次,可以知道 1 到 k-1 这些数和 \(a_{k+1}\) 的值相同或不同。

总共就问了 \(n\) 次。

这样就把所有数划分为两个阵营,一个阵营的数值全为0,另一个全为1。

然后根据base的值,它由 \(k\) 个数的和组成,而 \(k\) 为奇数,所以根据这k个数包含奇数个1,偶数个0(和为奇数),或者偶数个1、奇数个0(和为偶数)的关系,就可以知道每个阵营的数的值。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1005 + 10;
int n, k;
int ans[N], result[N];
int base, tem, rec;
set<int> st[2];

inline int ask() {
    int res;
    cout << '?';
    for(int i = 1; i <= k; i++) {
        cout << ' ' << ans[i];
    }cout << endl;
    cin >> res;
    return res;
}

void solve() {
    for(int i = 1; i <= k; i++) ans[i] = i;
    base = ask();  // 1 to k

    st[0].insert(k);
    for(int i = k + 1; i <= n; i++) {
        ans[k] = i;
        tem = ask();
        if(tem == base) {
            st[0].insert(i);
            if(i == k + 1) rec = 0;
        }
        else {
            st[1].insert(i);
            if(i == k + 1) rec = 1;
        }
    }
    ans[k] = k;

    for(int i = k - 1; i >= 1; i--) {
        ans[i] = k + 1;
        tem = ask();
        if(tem == base) st[rec].insert(i);
        else st[1 - rec].insert(i);

        ans[i] = i;
    }

    int cnt = 0;
    for(int i = 1; i <= k; i++) {
        if(st[0].count(i) == 1) cnt++;
    }
    cnt %= 2;
    if(cnt == base) {
        //k=1
        for(auto x : st[0]) result[x] = 1;
    }
    else {
        //k=0
        for(auto x : st[1]) result[x] = 1;
    }
    
    cout << '!';
    for(int i = 1; i <= n; i++) cout << ' ' << result[i];
    cout << endl;
}

int main(){
    cin >> n >> k;
    solve();
	return 0;
} 

E

这题是赛后补的

E - Duplicate

给你一个数字串s(包含1到9),一次操作可以让 \(s_i\) 变成 \(s_{i+1}\)\(s_i\),最后一个字符去掉。问多少次操作可以让s变成长度为1,如果不可能,输出-1

做法:容易发现如果出现两个连续的非1数字,答案为-1

我们要求的是操作的次数,记为ans。

如果最后一个数是1,那么$$ans += 1$$
否则,$$ans += 1 + (s[i]-'1')*(ans+1)$$

理解:ans += 1,是删掉当前这个数的操作次数;非1的数x每次操作会增长x-1个1,此前已经操作了ans次,那么一个非1的数y总共增加 (x-1)(ans)次,又因为删掉当前这个数又会导致一次1数量的增长,所以ans+=(x-1)(ans+1)

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1005 + 10;
int n;
string s;

int main(){
    cin >> n >> s;
    for(int i = 0; i < s.length(); i++) {
        if(i && s[i] != '1' && s[i - 1] != '1') {
            cout << -1 << endl;
            return 0;
        } 
    }
    ll ans = 0;
    while(1) {
        while(s.length() > 1 && s.back() == '1') s.pop_back(), ans++;
        if(s.length() == 1) {cout << ans << endl; return 0;}
        int tem = s.back() - '0'; s.pop_back();
        ans += 1 + (tem - 1) * (ans + 1) % 998244353;
        ans %= 998244353;
    }
    cout << ans << endl;

	return 0;
} 

posted @ 2023-08-06 16:12  starlightlmy  阅读(45)  评论(0编辑  收藏  举报