Codeforces Round #814 (Div. 2) A-C

目录

A. Chip Game

题意:

思路:

code:

B. Mathematical Circus

题意:

思路:

code:

C. Fighting Tournament

题意:

思路:

code:

后记


题意:

从1,1走到n,m. 每次只能向右/上走奇数个方向. T先走. 如果下一步不能走了(已经在n,m了) 那么当前轮次的人输了. 输出谁会赢.

思路:

看样例直接找到规律

如果n+m是偶数(两个都是奇数或者都是偶数), 后手赢了.

本来写了一堆, 但发现写的太绕了...找个小规模的4x4 or 3x3的推一下就ok了.

code:

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

void solve() {
    ll a, b;
    cin >> a >> b;
    if ((a + b) % 2 == 0)
        cout << "Tonya\n";
    else
        cout << "Burenka\n";
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

B. Mathematical Circus

题意:

问你对于每个给出的n, k是否存在n/2对a,b满足(a+k)*b%4=0

存在输出yes和n/2组的a,b对, 否则no

思路:

开始读错题了, 以为不到n/2对的n也算是yes有多少输出多少. 被这个坑WA了2发.

观察下式子, 显然只要式子的乘号左右两边有一个能整除4即可.

1)我们发现k %= 4 or k = 0是一直不合法的.

2)k % 2 == 1的话证明k是奇数, 那么(奇数 + 奇数) * 偶数数, 乘号左面一定是偶数, 那么两边都一定是2的倍数, 故可以提出4.满足

3)构造乘号左/右整除4的情况. easy. but注意构造a+k整除4时候用i=4 - k % 4为起点, i每次加4来构造比较方便

code:

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

void solve() {
    int n, k;
    cin >> n >> k;

    if (k % 2 == 1) {
        cout << "YES\n";
        for (int i = 1; i <= n - 1; i += 2) {
            cout << i << " " << i + 1 << "\n";
        }
    } else if (k % 2 == 0) {
        if (k % 4 == 0 || k == 0) {
            cout << "NO\n";
            return;
        }

        cout << "YES\n";
        for (int i = 4; i <= n; i += 4) {
            if (i % 4 == 0) {
                cout << i - 1 << " " << i<< "\n";
            }
        }
        
        for (int i = 4 - k % 4; i <= n; i += 4) {
            cout << i << " " << i - 1 << "\n";
        }
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

C. Fighting Tournament

题意:

给你一个序列a_1...a_n, 每次把前两个进行比较(a_1,a_2)小的放到队伍的最后面, 大的放到队伍的最前面, 大的击败数加一. 可以进行无限次. 给你q次询问, 每次问你第p个, 在进行了k次循环后所击败的人的数量.

思路:

卡的一点在:可能第k次循环之前已经开始击败了别人了或者第k次循环还没开始击败别人, 不能用k作为查询的目标点.

冗长版本解释:

    首先, 观察我们可以发现一件事情: 当最大的循环到第一位时, 之后的击败全部由最大的贡献! 对于每个人他的击败都一定是连续的!因为他被人打败后就一定不能再次成为队首去打败别人了

    其次, 想到对于每个a_i的每次击败, 我们都记录这次击败的所处循环次数是哪次. 查询的时候再判断目标循环次数k和a_p记录的产生击败的回合之间的关系得到答案. 换句话来说就是记录每个a_i在哪些回合(循环)产生击败! 第二句结合代码比较容易明白.

    最后, 我们发现对于k大于进行的轮数n-1时, 如果当前询问的p是最大值时, 存在以下: 最大值成为队首后一直打败别人的轮数 = 总轮数 - 成为队首所需轮数, 即 k - (n - 1)次. 把这个加到ans中即可(因为ans只记录n-1次循环进行后的结果)

简化版:

    1)对n-1次循环(1~n-1), 对于每个循环的获胜者, 将这个循环的编号加到这个获胜者中.

    2)对于每次询问, 找到a_p获胜的场次中第一个大于等于k的下标, 从0到这个下标-1即为1~k次循环后a_p的答案(但a_p为最大值时不包括1~n-1次之后到k的), 即直接用upperbound的到的值表示.

    3)对上述括号情况进行增添!

一点useful的事情 upper_bound在找不到指定元素大的时候会返回最后一个!
 

updata:简化了冗长的解释, 更新了输出, 更新了对没表述清楚的upperbound的解释(在代码中)

code:

#include <bits/stdc++.h>
 
using namespace std;
using ll = long long;
 
void solve() {
    int n, q;
    cin >> n >> q;
    
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
 
    vector<vector<int>> f(n);//记录每个人的每次击败
    int idx = 0;//记录当前最大的值是谁
    for (int i = 1; i < n; i++) {
        if (a[idx] < a[i]) {
            idx = i;
        }
        f[idx].push_back(i);
    }
 
    while (q--) {
        int p, k;
        cin >> p >> k;
        p --; //因为上面我a数组从0开始的下标
 
        
        int ans = upper_bound(f[p].begin(), f[p].end(), k) - f[p].begin(); //红字部分
        /*
        新增upper_bound函数解释:
        这个不是用来求大于等于k的值是谁的, 而是计算出第一个大于k的下标是谁!
        即用来计算<=k的一共有多少个->推出k轮能够产生多少个答案.
        因此我们 upperbound-f[p].begin(), 即可得到第一个大于k的下标, 
        因为第一个下标是0, 因此我们得到的下标正好是ans的数量, 不用再去加1!
        */
        if (k - n + 1 > 0 && p == idx) { //是最大的那个 并且k要大于n次
            ans += k - n + 1;
        }
 
        cout << ans << "\n";
    }
}
 
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    int t;
    cin >> t;
    
    while (t--) {
        solve();
    }
 
    return 0;
}

后记

一些sad and interesting的事情:

昨天赛时我写的思路和这个差不多, 不过实现的又臭又长还有bug, 当时写完居然过了pretest. 屎山如下....

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

int a[200005];
int b[200005];
int beat[200005];
vector<int> H[200005];

void solve() {
    int n, q;
    int mx = 0, pos = 0, mi = 1e9;
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        if (a[i] > mx) {
            mx = a[i];
            pos = i;
        }
        mi = min(mi, a[i]);
        beat[a[i]] = 0;
        H[i].clear();
        b[i] = a[i];
    }

    int round = 0;
    map<pair<int,int>, int>f;
    map<int, int>now;
    for (int i = 2; i <= pos; i++) {
        if (a[i] > a[1]) {
            a[1] = a[i];
            beat[a[i]] ++;
        } else {
            beat[a[1]] ++;
        }
        f[{a[1], ++round}] = beat[a[1]];
        H[a[1]].push_back(round);
    }

    while (q--) {
        int p, k;
        cin >> p >> k;

        if (p == 1 && b[1] == mx) {
            cout << k << "\n";
            continue;
        }

        vector<int> tmp = H[b[p]];
        if (tmp.size() == 0) {
            cout<<"0\n";
            continue;
        }
        
        if (k > tmp[tmp.size()-1]) {
            if (b[p] == mx) {
                cout << k - round + 1 << "\n";
            } else {
                cout << beat[b[p]] << "\n";
            }
        } else if (k == tmp[tmp.size()-1]){
            cout << f[{b[p], k}] << "\n";
        } else {
            int prev = 0;
                for (auto x : tmp) {
                    if (x <= k) {
                        prev = x;
                    } else break;
                }
                cout << f[{b[p], prev}] << "\n";
        }

    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}

写完我一下子就想出了hack数据如下, 我当时想着T的原因是我当时在每次查询有个for(auto) 显然会t在这里, 但来不及改了想着一定FST了...

1

100000 100000

99999 1 2 3..... 100000

1 1

1 2

...

1 99999

结果居然没有FST!!!!  震惊!!!!!

今天五点多交了个相同的code, 一看TLE了....估计是author更新了大数据

于是我开始改我的屎山, 改了两个多点, 改到吐WA了无数发如图, 还**T.

我发现可能是我的实现有大问题. 果断学习大佬们的写法, 狠狠A了hhh. 现在还在感叹于大佬们如此nb orz.

ok, 选课开始了, run去选课了owo. (对了, D1, D2今天需要补掉, 别懒.)

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值