← 返回题目页

A1570 优美的序列

标程

// A1570 优美的序列 —— 计数 + 单点修改
//
// 优美的序列:存在 i,使得前 i 个数严格递减、后面的数严格递增(V 型,谷底是最小值)。
//
// 结论:设各个值的出现次数为 cnt[v],最小值为 mn。
//   1) 谷底就是最小值,且最小值只能出现一次,故 cnt[mn] 必须为 1,否则答案为 0;
//   2) 其它每个值最多出现两次(左边一个、右边一个):
//        出现 1 次:可以放左边或右边,2 种选择;
//        出现 2 次:必须左右各一个,1 种选择;
//        出现 >= 3 次:无解,答案为 0。
//   于是答案 = 2^(cnt 为 1 的值的个数 - 1) mod p。
//
// 修改用计数数组维护:cnt 为 1 的值的个数 s1、cnt >= 3 的值的个数 s3,
// 最小值用一个有序集合(存所有出现过的值)维护。

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

typedef long long ll;

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

    int n, m;
    ll p;
    cin >> n >> m >> p;

    vector<int> a(n + 1), cnt(n + 2, 0);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        cnt[a[i]]++;
    }

    int s1 = 0;  // 出现次数恰为 1 的值的个数
    int s3 = 0;  // 出现次数 >= 3 的值的个数
    set<int> present; // 当前出现过的值(用来取最小值)
    for (int v = 1; v <= n; v++) {
        if (cnt[v] == 1) s1++;
        if (cnt[v] >= 3) s3++;
        if (cnt[v] > 0) present.insert(v);
    }

    auto modpow = [&](ll base, ll e) {
        ll r = 1 % p;
        base %= p;
        while (e > 0) {
            if (e & 1) r = r * base % p;
            base = base * base % p;
            e >>= 1;
        }
        return r;
    };

    // cnt[v] 增加 delta 时同步维护 s1、s3 和 present
    auto change = [&](int v, int delta) {
        int before = cnt[v];
        if (before == 1) s1--;
        if (before >= 3) s3--;
        if (before == 0 && delta > 0) present.insert(v);
        cnt[v] += delta;
        int after = cnt[v];
        if (after == 1) s1++;
        if (after >= 3) s3++;
        if (after == 0) present.erase(v);
    };

    auto answer = [&]() -> ll {
        if (s3 > 0) return 0;              // 有某个值出现 >= 3 次,无解
        int mn = *present.begin();          // 当前最小值
        if (cnt[mn] != 1) return 0;         // 最小值必须只出现一次
        return modpow(2, s1 - 1);           // 其余「只出现一次」的值各有 2 种选择
    };

    cout << answer() << '\n';
    for (int i = 0; i < m; i++) {
        int x, k;
        cin >> x >> k;
        change(a[x], -1);
        change(k, 1);
        a[x] = k;
        cout << answer() << '\n';
    }
    return 0;
}

动画演示