跳转至

第二届包子杯

2025-05-11

gym: https://codeforces.com/gym/105883

A 和 K 之后再来补。

E

挺简单的, 直接模拟就行, 但是要分解质因子, 只分解因子是会超时的, 质因子数量很少, 大概 \(\log n\), 不能嫌麻烦, 一下就写完了。

#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
using ll = long long;

constexpr int N = int(5e5) + 5;
vector<set<pii>>st(N);

int minp[N + 1];

void solve()
{
    int n;
    cin >> n;
    auto Ins = [&](int x, int y){
        auto it = st[x].lower_bound({y, -1});
        if(it == st[x].end() || (*it).first != y)
            st[x].insert({y, 1});
        else{
            auto [u, v] = *it;
            st[x].erase(it);
            st[x].insert({y, v + 1});
        }
    };
    auto Del = [&](int x, int y){
        auto it = st[x].lower_bound({y, -1});
        auto [u, v] = *it;
        st[x].erase(it);
        if(v - 1)
            st[x].insert({y, v - 1});
    };
    while(n--){
        char op;
        cin >> op;
        if(op == '+'){
            int x, y;
            cin >> x >> y;
            while(x != 1){
                int p = minp[x];
                while(x % p == 0)
                    x /= p;
                Ins(p, y);
            }
        }else if(op == '-'){
            int x, y;
            cin >> x >> y;
            while(x != 1){
                int p = minp[x];
                while(x % p == 0)
                    x /= p;
                Del(p, y);
            }
        }else{
            int k;
            cin >> k;
            int ans = 0;
            while(k != 1){
                int p = minp[k];
                while(k % p == 0)
                    k /= p;
                if(st[p].size())
                    ans = max(ans, (*st[p].rbegin()).first);
            }
            cout << ans << '\n';
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    for(int i = 2; i <= N; i++){
        if(minp[i]) continue;
        minp[i] = i;
        if((ll)i * i > N) continue;
        for(int j = i * i; j <= N; j += i)
            minp[j] = i;
    }
    int T = 1;
    // cin >> T;
    while(T--)
        solve();
    return 0;
}

G

感觉思路非常简单, 也确实非常简单, 但是我们一直 WA, WA麻了。。。然后换 py 照着重写但是过了。。。刚才看了下才发现, 是因为我没有记清楚 long longunsigned long long 的范围。。。

\(n\) 的上界是 \(2^{64} - 1\), 所以最多会有 \(64\) 位, 我们可以进行 \(63\) 次询问, 那肯定一位位顺着问。 我们只问 \(2 ^ i\) 的值, 如果 \(n\) 的这一位为 \(1\), 那么结果肯定会是实际的 \(1\) 的数量减一, 否则就是加一。

所以我们把高的 \(63\) 位都问完, 如果 \(n\)\(63\) 位里有 \(0\) 且有 \(1\), 那么这 \(63\) 次询问的结果一定只有两种值, 且它们相差 \(2\)。 否则这些位肯定全 \(0\) 或者全 \(1\), 这些需要特判一下, 注意 \(64\) 位全 \(1\)ULLONG_MAX 我们这里之前本来改过,,,后来又怀疑自己了。

然后我们问完之后就可以确定前 \(63\) 位的情况了, 最后一位我们只需要看我们已经填了的 \(1\) 的数量是否等于 \(n\)\(1\) 的数量。

py 其实也不是很熟,,,这个stdout.flush() 我不知道得 import 啥, 然后搜了一下才知道。 这回知道了。

import sys

for _ in range(int(input())):
    cnt = [0] * 64

    for i in range(1, 64):
        print("?", pow(2,i))
        sys.stdout.flush()
        cnt[i] = int(input())

    mx = 0
    mn = 64

    for i in range(1, 64):
        mx = max(mx, cnt[i])
        mn = min(mn, cnt[i])

    ans = 0

    if(mx == mn):
        if mx == 1:
            ans = 0
        elif mx == 2:
            ans = 1
        else:
            ans = pow(2, 64) - 1

    else:
        tnc = 0
        for i in range(1, 64):
            if cnt[i] == mn:
                tnc += 1
                ans += pow(2, i)

        if tnc != mn + 1:
            ans += 1

    print("!", ans)
    sys.stdout.flush()

然后比赛时我都怀疑自己编译器有问题了。。。 因为我写 for 循环, 如果只有一行我就不喜欢打括号, 然后当时写一个 for 循环, 忘记删括号了。

#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
using ll = long long;
using ull = unsigned long long;

int ask(ull x)
{
    cout << "? " << x << endl;
    cout.flush();
    int res;
    cin >> res;
    return res;
}

void solve()
{
    ull ans = 0;
    vector<int> cnt(64);
    for(int i = 63; i >= 1; i--){
        cnt[i] = ask(1ull << i);
    }
    int mx = *max_element(cnt.begin() + 1, cnt.end());
    int mn = *min_element(cnt.begin() + 1, cnt.end());

    if(mx == mn){
        if(mx == 1) ans = 0;
        else if(mx == 2) ans = 1; 
        else ans = ULLONG_MAX;
    }else{
        int tnc = 0;
        for(int i = 63; i >= 1; i--){
            if(cnt[i] == mn){
                tnc++;
                ans |= 1ull << i;
            }
        }
        if(tnc != mn + 1) ans |= 1, tnc++;
        assert(tnc == mx - 1);
    }
    cout << "! " << ans << endl;
    cout.flush();
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T = 1;
    cin >> T;
    while(T--){
        solve();
    }
    return 0;
}

H

第一道 AC 的题, 但是四十多分钟才AC 且 WA两次, 虽然难度不高。 队友想出来的, 太强。

感觉我构造太菜, 不过这种只要不着急慢慢试, 基本都能试出来的, 影响的只是时间, 如果写得太慢, 会没有状态和时间写后面的题目。

构造方法有很多。

#include<bits/stdc++.h>
using namespace std;

void solve()
{
    int n;
    cin >> n;
    if(n == 2){
        cout << "-1\n";
        return;
    }
    if(n == 4){
        cout << "3 4 1 2\n";
        return;
    }
    if(n & 1){
        for(int i = n; i > n / 2 + 1; i--){
            cout << i << " ";
        }
        cout << 1 << ' ';
        for(int i = 2; i <= n / 2 + 1; i++)
            cout << i <<  " ";
        cout << '\n';
    }else{
        cout << n << " " << n / 2 << ' ';
        for(int i = 1; i < n / 2; i++)
            cout << i << " ";
        for(int i = n - 1; i > n / 2; i--)
            cout << i << " ";
        cout << "\n";
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T;
    cin >> T;
    while(T--)
        solve();
    return 0;
}

I

出题人以为的签到题, 但最后只有几个队提交了。 群友还是都太强了...

虽然刚才看了下, 感觉最难的是推出那个公式, 就是知道正方形的两个对角线的顶点的坐标, 怎么求剩下的两个顶点的坐标。。。最智慧的一集。

一个点绕一个点旋转 \(90\) 度的话, 新的坐标其实是可以求出来的。而且这个题这里, 也可以用向量来求。

知道正方形两个对角的顶点 \((x1, y1)\)\((x2, y2)\), 那么剩下两个点的坐标就是 \((\frac{x1+x2}{2}-\frac{y1-y2}{2},\frac{y1+y2}{2}+\frac{x1-x2}{2})\)\((\frac{x1+x2}{2}+\frac{y1-y2}{2},\frac{y1+y2}{2}-\frac{x1-x2}{2})\)

比赛其实我们也没怎么读这题, 因为确实没啥人开。

#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
#define fi first
#define se second

void solve()
{
    // 只用枚举主对角线的两个点
    int n;
    cin >> n;
    vector<pii> p(n);
    for(int i = 0; i < n; i++)
        cin >> p[i].first >> p[i].second;
    map<pii, int>mp;
    for(int i = 0; i < n; i++){
        mp[p[i]] = i;
    }
    int ans = 0;
    vector<int> cnt(n);
    for(int i = 0; i < n; i++){
        auto [x1, y1] = p[i];
        for(int j = i + 1; j < n; j++){
            auto [x2, y2] = p[j];
            int x3 = (x1 + x2) - (y1 - y2);
            int y3 = (y1 + y2) + (x1 - x2);
            int x4 = (x1 + x2) + (y1 - y2);
            int y4 = (y1 + y2) - (x1 - x2);
            if((x3 & 1) || (y3 & 1) || (x4 & 1) || (y4 & 1)) continue;
            x3 /= 2, y3 /= 2, x4 /= 2, y4 /= 2;
            if(mp.contains({x3, y3}) && mp.contains({x4, y4})){
                // cerr << format("{}, {}, {}, {}", x1, y1, x2, y2) << '\n';
                cnt[i]++, cnt[j]++;
                cnt[mp[{x3, y3}]]++, cnt[mp[{x4, y4}]]++;
                ans++;
            }
        }
    }
    // cerr << ans << '\n';
    // for(int i = 0; i < n; i++)
    //     cerr << format("{}", cnt[i]) << " \n"[i + 1 == n];
    cout << (ans - *min_element(cnt.begin(), cnt.end())) / 2 << '\n';
}
int main()
{
    // freopen("1.in", "r", stdin);
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T;
    cin >> T;
    while(T--)
        solve();
    return 0;
}

J

挺简单, 具有单调性, 可以二分, 查区间最大值, 只能去最大值的地方。

也可以单调栈 \(O(n)\) 写。 我刚开始真直接写线段树查区间最值来着。。。 但这题两个 log 会 T, 然后改成 ST 表过了。。。 不写 ST 表是因为平常没咋用过不熟, 线段树写得比较熟。。。 像这种也是得练练。

#include<bits/stdc++.h>
using namespace std;

#define ls p << 1
#define rs p << 1 | 1
#define mi ((l + r) >> 1)

constexpr int N = int(1e6) + 10;
int a[N], n, ST[N][25];
void init()
{
    int p = log2(n);
    for(int i = 1; i <= n; i++)
        ST[i][0] = a[i];
    for(int k = 1; k <= p; k++){
        for(int s = 1; s + (1 << k) <= n + 1; s++){
            ST[s][k] = max(ST[s][k - 1], ST[s + (1 << (k - 1))][k - 1]);
        }
    } 
}
int query(int L, int R)
{
    int k = __lg(R - L + 1);
    return max(ST[L][k], ST[R - (1 << k) + 1][k]);
}
// tr[N << 2];
// void build(int p, int l, int r)
// {
//     if(l == r){
//         tr[p] = a[l];
//         return;
//     }
//     build(ls, l, mi);
//     build(rs, mi + 1, r);
//     tr[p] = max(tr[ls], tr[rs]);
// }
// int query(int p, int l, int r, int lx, int rx)
// {
//     if(l >= lx && r <= rx)
//         return tr[p];
//     int res = 0;
//     if(lx <= mi) res = max(res, query(ls, l, mi, lx, rx));
//     if(rx > mi) res = max(res, query(rs, mi + 1, r, lx, rx));
//     return res;
// }


void solve()
{
    // int n;
    cin >> n;
    vector<int> idx(n + 1);
    for(int i = 1; i <= n; i++)
        a[i] = i;
    // shuffle(a + 1, a + 1 + n);
    for(int i = 1; i <= n; i++){
        cin >> a[i];
        idx[a[i]] = i;
    }
    // build(1, 1, n);
    init();
    if(n == 1){
        cout << "0\n";
        return;
    }
    int lo = 0, hi = n + 1;
    auto check = [&](int d){
        vector<bool> ok(n + 1);
        for(int i = idx[n]; i >= idx[n] - d && i >= 1; i--)
            ok[a[i]] = true;
        for(int i = idx[n]; i <= idx[n] + d && i <= n; i++)
            ok[a[i]] = true;
        for(int i = idx[n] - d - 1; i >= 1; i--){
            // int num = query(1, 1, n, max(i - d, 1), min(n, i + d));
            int num = query(max(i - d, 1), min(n, i + d));
            if(!ok[num]) return false;
            ok[a[i]] = true;
        }
        for(int i = idx[n] + d + 1; i <= n; i++){
            // int num = query(1, 1, n, max(i - d, 1), min(n, i + d));
            int num = query(max(i - d, 1), min(n, i + d));
            if(!ok[num]) return false;
            ok[a[i]] = true;
        }
        return true;
    };
    // check(1);
    while(lo < hi - 1){
        int mid = (lo + hi) >> 1;
        if(check(mid)) hi = mid;
        else lo = mid;
    }
    cout << hi << '\n';
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T;
    cin >> T;
    while(T--)
        solve();
    return 0;
}