Loading

AtCoder Beginner Contest 313




B - Who is Saikyo?

难度: ⭐

题目大意

选择有n个人, 给出m个强弱关系, 例如a比b强, 这种强弱关系是可以传递的, 如果b比c强, 那么a就b强; 请问这n个人中最强的是谁, 如果根据给出的强弱关系无法确定最强者, 那么就输出-1;

解题思路

我们可以用建树的思想找入度即可, a比b强就让b的入度加一, 最后如果只有一个人没有入度, 说明他就是最强的; 如果有多个说明无法确定;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 1e5+10;
typedef pair<int,int> PII;
int n,m;
bool st[N];
signed main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        st[b]=true;
    }
    int maxn=0;
    for(int i=1;i<=n;i++){
        if(!st[i]){
            if(maxn==0) maxn=i;
            else {
                cout<<-1;
                return 0;
            }
        }
    }
    cout<<maxn;
    return 0;
}




C - Approximate Equalization 2

难度: ⭐⭐⭐

题目大意

给定n个数, 每次操作我们可以选择其中的两个数, 让其中一个数+1, 另一个数-1; 问最少经过多少次这样的操作可以让这n数中的最大值和最小值之间差值最大为1;

解题思路

每次操作都会加一减一, 所有n个数的总和不会变, 而且n个数最后各自的差值不会大于1, 设最后的n个数是由x和x+1组成的; 那我们就可以直接让sum/n, 得出的数就是x; 接下来就要看会有多少个x+1, 这一步是整道题中最巧妙的地方, 那就是sum%n, 没错只需要让sum对n取余就可以得到最终序列中x+1的个数; 最后我们就可以把n个数从大到小排序, 前sum%n个数需要变化为x+1, 剩下的数需要变化为x; 最后把变化的次数除2就是最终答案;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5+10;
typedef pair<int,int> PII;
int n,m,idx;
int f[N];
signed main(){
    cin>>n;
    int sum=0;
    for(int i=1;i<=n;i++) {
        cin>>f[i];
        sum+=f[i];
    }
    sort(f+1,f+1+n,greater<>());
    int x = sum/n;
    int y = sum%n;
    for(int i=1;i<=n;i++){
        if(i<=y) idx+=abs(f[i]-(x+1));
        else idx+=abs(f[i]-x);
    }
    cout<<idx/2;
    return 0;
}




D - Count Bracket Sequences

难度: ⭐⭐⭐

题目大意

交互题; 现在有一个未知的长度为n且由0/1组成的数列, 我们每次要问k个数: x1,x2...xk; 系统会把所有Ai加起来(i = x1,x2...xk), 如果和为偶数就输出0, 奇数输出1; 最多可以问n次, 最后输出这个未知的数列;
注意: 假设我们询问了x次, 那么根据这x个回答所对应的数列必须是唯一确定的, 如果有多个满足回答的序列, 而你输出了其中一个, 这会被判定为wa, 就像样例那样;

解题思路

一道思维题, 由于每次必须问k个数, 那么想象一个长度为k的滑块, 从下标1开始, 一直向右滑到下标n, 每次移动一个下标, 该滑块的移动距离就是n-k+1(滑块的右端点初始位置在k, 最终在n); 这也就对应了(n-k+1)次询问; 第一次是(1 ~ k), 第二次是(2 ~ k+1) ... 最后是(n-k+1 ~ n); 我们观察(1 ~ k)和(2 ~ k+1)这两次询问, 这相当于是两个区间和S1和S2, S2 = S1 - A1 + Ak+1; 如果两次询问的结果相同, 说明A1和Ak+1的奇偶性一致(都是0或者都是1), 反之则不一致; 由上所述, 通过这两次询问, 我们可以让A1和Ak+1建立起关系, 继续往后就可以让A2和Ak+2, A3和Ak+3...都建立起关系, 总而言之, 后K个数可以用前K个数来求(Ak+i可以用Ai来求); 这样我们只需要求出前k个数是多少, 后面的自然就都知道了;
现在我们把目光放在前k+1个数上, 上面的逻辑关键在于 S2 = S1 - A1 + Ak+1; 这是因为第二次询问中我们去掉了A1加上了Ak+1, 从而得到了他俩之间的关系, 那我们照样可以去掉A2加上Ak+1, 这样就可以得到A2和Ak+1之间的联系, 用这种方法, 我们再进行k-1次询问就可以知道前k个Ai和Ak+1的关系; 这和之前那些询问的次数相加正好是n次; 在得到前k个Ai和Ak+1的关系后, 我们就可以把前k个Ai分为两类a和b(a表示和Ak+1相等, b表示不等); 题目说k一定是一个奇数, 那么a和b的数量一定是一个奇数一个偶数, 所以如果前k个数的总和是奇数, 那么数量是奇数的那一类就是1, 反之就是0; 这样我们就得到了前k个数的具体的数字, 再用他们递推求后面的数即可;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
//#define endl '\n'
using namespace std;
const int N = 2e5 + 10;
typedef pair<int, int> PII;
int n, m,k, idx;
int f[N], s[N], fst[N],st[N];
void check(int n1, int n0) {
    int x, y;
    if (f[k] == 1) y = 1;
    else y = 0;
    if (n1 % 2 == y) x = 1;
    else x = 0;
    for (int i = 1; i <= k; i++) {
        if (fst[i] == x) s[i] = 1;
        else s[i] = 0;
    }
}
signed main() {
    cin >> n >> k;
    for (int i = 1; i <= n - k + 1; i++) {
        cout << '?';
        for (int j = i; j <= i + k - 1; j++) {
            cout << ' ' << j;
        }
        cout << endl;
        cin >> f[i + k - 1];
    }
    st[1] = f[k + 1];
    for (int i = 2; i <= k; i++) {
        cout << '?';
        for (int j = 1; j <= k+1; j++) {
            if (j == i) continue;
            cout << ' ' << j;
        }
        cout << endl;
        cin >> st[i];
    }
    int n1 = 0, n0 = 0;
    for (int i = 1; i <= k; i++) {
        if (f[k] == st[i]) {
            fst[i] = 1, n1++;
        }
        else {
            fst[i] = 0, n0++;
        }
    }
    check(n1, n0);
    for (int i = k + 1; i <= n; i++) {
        if (f[i] == f[i - 1]) s[i] = s[i - k];
        else s[i] = !s[i - k];
    }
    cout << '!';
    for (int i = 1; i <= n; i++) {
        cout << ' ' << s[i];
    }
    
    return 0;
}




E - Duplicate

难度: ⭐⭐⭐

题目大意

给定一个由1~9组成的数列, 其每次更新的方式如下:
除了数列的最后一个数, 对于剩下的数si, 把si变成si+1个si, 然后删除最后一个数; 直到数列的长度变成1, 问需要更新多少次, 如果长度永远不会变成1, 就输出-1;
eg: 313第一次更新会变成3111, 第二次更新是311, 第三次是31, 第四次是3; 所以需要4次;

解题思路

通过简单的证明我们可以发现, 对于两个相邻的数, 如果他们都不为1, 那么就永远不会变成长度为1, eg: 22; 所以我们可以两个两个遍历, 只要两个其中有一个1就行;
如果其中一个为1, 有两种形式"x 1"和"1 x"; 对于"x 1"的结果就是x, "1 x"的第1次更新是变为x个1, 第n次更新就是 n(x-1)+1 个1; 所以我们可以倒着遍历, 如果si=1, 那么我们就需要n(si-1-1)+1 次来清除这些1; 如果si=x, 说明si+1=1, 那么只需要更新一次就可以把si去掉; 遍历完之后我们就得到了长度为0需要更新多少次, 在减一就是长度为1时的次数;

神秘代码

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
typedef pair<int, int> PII;
int n, m, idx;
signed main() {
    cin >> n;
    string s;
    cin >> s;
    s = ' ' + s;
    for (int i = 2; i <= n; i++) {
        if (s[i] != '1' && s[i - 1] != '1') {
            cout << -1;
            return 0;
        }
    }
    idx = 1;
    for (int i = n-1; i >= 1; i--) {
        if (s[i] == '1') idx = (idx + idx * (s[i + 1] - '0' - 1) + 1) % mod;
        else idx = (idx + 1) % mod;
    }
    cout << idx -1;
    return 0;
}




F - Duplicate

难度: ⭐⭐⭐⭐⭐

posted @ 2023-11-01 18:32  mostimali  阅读(10)  评论(0编辑  收藏  举报