Contestant(alt). Rank 5166. Rating -27(+123 -150).

A. Plus or Minus

题意

给定三个数 \(a, b, c\),判断 \(a+b = c\) 还是 \(a - b = c\),前者输出 \(+\),后者输出 \(-\),保证有解。

思路

如题。

时间复杂度:\(O(1)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>

const int N = 1.5e6 + 10, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int a, b, c;
    cin >> a >> b >> c;
    if(a + b == c) cout << '+' << '\n';
    else cout << '-' << '\n';
}

signed main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t --) solve();
}

这才应该是低程的签到题啊((

B. Grab the Candies

题意

给定一个序列 \(a\),按次序拿出 \(a_i\),定义 \(a_i\) 为偶数时 \(A\) 拿走 \(a_i\),否则 \(B\) 拿走 \(a_i\)。输出是否可以将序列重新排列,使得每次取走后 \(A\) 持有的总和严格大于 \(B\)

思路

如题。

很显然,我们先全排满偶数,再排奇数即可。

那么,题目转化为偶数的总和和奇数的总和的比较。

时间复杂度:\(O(n)\)  

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>

const int N = 1.5e6 + 10, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    int sum1 = 0, sum2 = 0;
    for(int i=0;i<n;i++){
        int cur;
        cin >> cur;
        if(cur % 2 == 0) sum1 += cur;
        else sum2 += cur;
    }
    cout << (sum1 > sum2 ? "YES\n" : "NO\n");
}

signed main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t --) solve();
}

脑子得稍微动一下的题(

C. Find and Replace

题意

给定一个字符串,规定可将一种字母全部替换为 \(0\)\(1\),在全部替换后,输出是否可以将字符串变为相邻字符不相同的字符串,即 \(0101\) 相间。

思路

显然,我们只需对每一个字母去考虑,如果任意两个距离最近的相同的字母的距离为奇数,那么就构造不出来,否则一定可以。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>

const int N = 1.5e6 + 10, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    vector<int> pre(26, -1);
    string s;
    cin >> s;
    bool f = true;
    for(int i=0;i<n;i++){
        int cur = s[i] - 'a';
        if(pre[cur] != -1 && (i - pre[cur]) % 2 == 1){
            f = false;
            break;
        }
        pre[cur] = i;
    }
    cout << (f ? "YES\n" : "NO\n");
}

signed main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t --) solve();
}

差点看错题

D. Odd Queries

题意

给定一个序列,对于 \(q\)独立 询问,给定一段区间 \([l, r]\) 以及一个整数 \(k\),将区间内的数字全都修改为 \(k\) 后,输出序列的总和是否为奇数。

思路

既然询问是独立的,那么我们直接对所有询问单独考虑即可。

也就是说,我们不需要模拟修改,而只需求出值。

那么,很简单,我们求出前缀和,然后用 \(O(1)\) 的复杂度拿出区间前后的元素总和,最后判断加上区间内的值能否变为奇数即可。

时间复杂度:\(O(n + q)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>

const int N = 1.5e6 + 10, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n, q;
    cin >> n >> q;
    vector<int> sum(n + 1, 0);
    for(int i=1;i<=n;i++){
        int cur;
        cin >> cur;
        sum[i] = sum[i - 1] + cur;
    }
    while(q --){
        int l, r, k;
        cin >> l >> r >> k;
        cout << ((sum[l - 1] + (r - l + 1) * k + sum[n] - sum[r]) % 2 == 1 ? "YES\n" : "NO\n");
    }
}

signed main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t --) solve();
}

这题怎么更水了(

E. Interview

题意

互动游戏。

背景:给定几堆石子,每堆石子都由权值为 \(1\) 的石子堆叠得到,特殊地,有一堆石子中有一个石子的权值为 \(2\)

初始:给定堆数,以及每堆石子的 个数

互动:输出一段区间的左右端点,返回这段区间内所有石子的 权值和

限制:最多 \(30\) 次。

目标:输出特殊的石子在哪个堆里。

思路

首先,我们可以跑一遍前缀和,方便后续查询。

其次,如果我们询问了区间 \([1, k]\),得到的值等于个数,那么我们可以断定特殊的石子在 \([k + 1, n]\) 里,反之就在前者里。

那么,对于 \(k\) 的选择,我们直接用二分的思路做即可。

最后二分的结果就是答案。

次数为 \(\log_2n\),不超过 \(30\)

时间复杂度:\(O(t \log n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>

const int N = 1.5e6 + 10, inf = 0x3f3f3f3f3f3f3f3f;

void solve(){
    int n;
    cin >> n;
    vector<int> sum(n + 1, 0);
    for(int i=1;i<=n;i++) {
        int cur;
        cin >> cur;
        sum[i] = sum[i - 1] + cur;
    }
    int l = 1, r = n, mid;
    while(l < r){
        mid = (l + r) >> 1;
        cout << "? " << mid - l + 1 << ' ';
        for(int i=l;i<=mid;i++) cout << i << ' ';
        cout << '\n';
        cout.flush();
        int g;
        cin >> g;
        if(g == sum[mid] - sum[l - 1]) l = mid + 1;
        else r = mid;
    }
    cout << "! " << l << '\n';
}

signed main(){
    //ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while(t --) solve();
}

我怎么会想着去在两边都询问一次呢,我好铸

F. Bouncy Ball

题意

给定一个矩阵,以左上角为原点建立坐标系,给定两个点 \((i_1, j_1), (i_2, j_2)\),以及初始运动方向 \(s, s \in \{DL, DR, UL, UR\}\)\(DL\) 表示向左下角走,也就是 \((i, j) \rightarrow (i + 1, j - 1)\),其余同理)。在碰到边界后,会发生反弹,如向左上角走,碰到上边界后会反弹,向左下角继续移动。在碰到边角后,运动方向反转,此处记反弹次数为 \(1\) 次,而不是 \(2\) 次。

输出从 \((i_1, j_1)\) 开始运动,到第一次到达 \((i_2, j_2)\) 后,经历了多少次反弹。

无法到达输出 \(-1\)

思路

数据量并不大,直接模拟即可。

在模拟的时候,我们记录是否已经从某个方向运动到该格子。那么,只要碰到被我们记录过的格子,只要方向一致,那么就出现了循环,直接打断即可。

时间复杂度:最坏\(O(4nm)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

const int N = 2e5 + 10, inf = 0x3f3f3f3f3f3f3f3f;

void init(){}

void solve() {
    int n, m, i, j, si, sj;
    string ori;
    cin >> n >> m >> i >> j >> si >> sj >> ori;
    bool vis[n + 1][m + 1][4] = {};
    int d = ((ori[0] == 'D' ? 0 : 1) << 1) + (ori[1] == 'R' ? 0 : 1); //00 DR, 01: DL, 10: UR, 11: UL
    int cnt = 0;
    while (!vis[i][j][d]) {
        if (i == si && j == sj) {
            cout << cnt << '\n';
            return;
        }
        vis[i][j][d] = true;
        int new_d = d;
        if ((d == 0 || d == 1) && i == n) new_d += 2;
        if ((d == 0 || d == 2) && j == m) new_d ++;
        if ((d == 1 || d == 3) && j == 1) new_d --;
        if ((d == 2 || d == 3) && i == 1) new_d -= 2;
        if (new_d != d) cnt++;
        d = new_d;
        if (d % 2 == 1) j--;
        else j++;
        if ((d >> 1) == 1) i--;
        else i++;
    }
    cout << -1 << '\n';
}

signed main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    init();
    int t;
    cin >> t;
    while(t --) solve();
}

居然,居然,居然就是暴力...

G1. Subsequence Addition (Easy Version)

详见G2,区别是G1的数据量更小

G2. Subsequence Addition (Hard Version)

题意

给定一个初始只含一个数字 \(1\) 的序列,规定操作为选择序列中的任意元素,将选定元素之和加入序列中。

现在,给定一个序列,输出其是否为上述操作所得。

思路

我们可以贪心地认为,排序之后,较大的数一定小于等于所有比他小的数的和,而且只要满足这个条件,一定有一种方案得到这个数。

至于为什么,我们可以想想二分,序列中的数一定是某些数的和,那么如此拆分下去,就是一群 \(1\) 的和,而且数量的集合覆盖 \([1, sum]\),所以是成立的。

具体的证明需要用到归纳法。

因此,我们排个序,然后判断所有 \(a_i\) 是否大于等于 \(sum_{i - 1}\) 即可。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

const int N = 2e5 + 10, inf = 0x3f3f3f3f3f3f3f3f;

void init(){}

void solve(){
    int n;
    cin >> n;
    vector<int> a(n);
    for(int i=0;i<n;i++) cin >> a[i];
    sort(a.begin(), a.end());
    int sum = 1;
    bool f = a[0] == 1;
    for(int i=1;i<n;i++){
        if(!f) break;
        if(i < 2 && a[i] != 1 || a[i] > sum) f = false;
        sum += a[i];
    }
    cout << (f ? "YES\n" : "NO\n");
}

signed main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    init();
    int t;
    cin >> t;
    while(t --) solve();
}

很有趣一贪