题解:AT_abc391_c [ABC391C] Pigeonhole Query

额这篇和上一篇当初不知道为什么没有放到博客园上,现在才想起来,见谅。

question

\(N\) 只鸽子,编号从 \(1\)\(N\),有 \(N\) 个鸽巢,编号从 \(1\)\(N\)。最初,鸽子 \(i\) 在巢 \(i(1\leq i\leq N)\) 中。

您会收到 \(Q\) 个查询,您必须按顺序处理这些查询。查询有两种类型,每种都以下列格式之一给出:

  • 1 P H :将鸽子 \(P\) 移到鸽巢 \(H\) 中。
  • 2 :输出包含一只以上鸽子的鸽巢数量。

solution

首先我们解决操作 \(1\)

我们让 \(a_i\) 表示巢 \(i\) 的鸽子数量,同时让 \(b_i\) 表示鸽子 \(i\) 目前所处的巢相对于初始的巢 \(i\) 偏移了多少。

那么我们可以得到以下代码。

if (opt == 1){
    int p, h;
    cin >> p >> h;
    int now = p + b[p];// 鸽子 p 目前所处的巢的编号为 now
    a[now]--;
    a[h]++;
    b[p] = h - p;// 更新b[i]
}

接下来我们考虑操作 \(2\)

显然不能每次的扫一遍 \(a\) 数组,所以我们在操作 \(1\) 中加一步。对于每次操作 \(1\),有可能对答案做出贡献的只有 \(a_{now}\)\(a_h\),所以我们进行分类讨论。

  • \(a_{now}\) 操作完后等于 \(1\),则说明 \(a_{now}\) 操作之前一定符合条件,那么我们让答案减 \(1\)

  • \(a_h\) 操作完后大于 \(1\),且 \(a_h\) 操作之前不符条件,那么我们让答案加 \(1\)

为了方便,我们记录 \(f_i\) 表示巢 \(i\) 当前是否符合条件。整体思路出来了,以下是完整代码。

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

#define int long long

const int N = 1e8 + 5;

int n, q; 
int a[N], b[N];
int cnt;
bool f[N];

signed main(){
    cin.tie(0)->sync_with_stdio(0);

    cin >> n >> q;

    for (int i = 1; i <= n; i++)// 给 a 数组付赋初值
        a[i] = 1;

    while (q--){
        int opt;
        cin >> opt;
        if (opt == 1){
            int p, h;
            cin >> p >> h;
            int now = p + b[p];
            a[now]--;
            a[h]++;
            b[p] = h - p;
            if (a[now] == 1) cnt--, f[now] = 0;
            if (a[h] > 1 && !f[h]) cnt++, f[h] = 1;
        }
        else if (opt == 2)
            cout << cnt << "\n";
    }

    return 0;
}
posted @ 2025-02-06 15:35  chaqjs  阅读(34)  评论(0)    收藏  举报