• CodeForces 1632D New Year Concert(ST 表、二分)


    D - New Year Concert

    题目大意:

    给出一个序列 \(a\)

    对于一个序列 \(b\),如果其中存在一个区间 \([L, R]\),满足区间 \(gcd\) 等于区间长度 \(R - L + 1\),则认为这个序列是不好的。

    每次修改可以将任意一个数改成任意正整数。

    现求对序列 \(a\) 的每一个前缀最少需要修改多少次,使得该前缀是一个好序列。

    思路:

    通过简单模拟一下样例 \(3\),我们可以发现。

    • \(f(x)\) 的状态继承了 \(f(x - 1)\) 的状态。并且假设每次答案最多增加 \(1\)(但还是不太懂为什么)
    • 对于一个区间 \([l , r]\),其区间 \(gcd\) 满足单调递增。
    • 为了让修改次数最少,我们可以每次将数修改为一个大质数,因为区间 \(gcd\) 单调递增,那么修改成大质数的话,我们就不用考虑这个质数所在位置及其前面的数字了,他们与区间右端点的 \(gcd\) 都为 \(1\),而要想满足是不好序列的条件只用检查区间端点。

    由于我们并不需要真的去修改一个数,我们可以用 ST 表预处理出区间 \(gcd\)

    那么,现在问题就在于当我们考虑以 \(i\) 位置为结尾的前缀时,我们如何求出 \([1, i]\) 这段区间上我们要修改几次。

    前面说到 \(f(x)\) 的状态可以由 \(f(x - 1)\) 的状态转移得到,并且我们在 \(x\) 之前可能会进行一些修改操作,将某些数字改成大质数,那么我们可以用变量 \(idx\) 记录上一次修改的位置,问题就由转化成如何求出在 \([idx, i]\) 这段区间上我们要修改几次。

    由单调性考虑二分,二分找是否存在一个位置 \(p\) ,使得 \([p, i]\) 是一个不合法区间。

    \(tmp\) 为区间 \([mid, i]\)\(gcd\)\(len\)\([mid, i]\) 的区间长度。

    因为 \(tmp\) 单调递增,\(len\) 单调递减,那么一定会出现以下的局面。

    ----------------|*|------------------*>
        tmp < len          tmp > len     i
                    不合法
    

    这样我们就能二分的找是否存在这个位置 \(p\)

    Code:
    class SparseTable {
    public:
        int lg[N] = {-1};
        ll st[24][N];
        template <class T>
        T op(T &a, T &b) { return gcd(a, b); } //检查区间操作!
        SparseTable() {
            for (int i = 1; i < N; i++) {
                lg[i] = lg[i / 2] + 1;
            }
        }
        inline void init(int n, vector<ll> &a) {
            //完成初始化! for i in [1, n]: st[0][i] = val[i], 对应区间 [i, i + 2^0 -1]
            for (int i = 1, x; i <= n; i++) {
                st[0][i] = a[i];
            }
            build(n);
        }
        inline void build(int n) {
            for (int i = 1; i <= lg[n]; i++)
                for (int j = 1; j + (1 << i) - 1 <= n; j++) 
                    st[i][j] = op(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
        }
        ll query(int l, int r) {
            ll len = lg[r - l + 1];
            return op(st[len][l], st[len][r - (1 << len) + 1]);
        }
    };
     
    SparseTable ST;
     
    int main() {
        ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
        int n;
        cin >> n;
        vector<ll> a(n + 1);
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
        }
     
        ST.init(n, a);
     
        ll ans = 0;
        int idx = 1; // 将a[idx - 1]修改为大质数
     
        for (int i = 1; i <= n; i++) {
            if (a[i] == 1) {
                ans++;
                idx = i + 1;
            } else {
                int l = idx, r = i; // 二分找是否存在一个位置p,使得[p, i]是一个bored区间
                bool cur = false;
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    int tmp = ST.query(mid, i);
                    int len = i - mid + 1;
                    if (tmp < len) {
                        l = mid + 1;
                    } else if (tmp == len) {
                        cur = true;
                        break;
                    } else {
                        r = mid - 1;
                    }
                }
                if (cur) {
                    ans++;
                    idx = i + 1;
                }
            }
            cout << ans << " \n"[i == n];
        }
        return 0;
    }
    
  • 相关阅读:
    十天冲刺个人博客四
    十天冲刺个人博客三
    十天冲刺个人博客二
    软件工程课堂七(单词统计)
    十天冲刺个人博客一
    软件工程第九周总结
    人月神话阅读笔记03
    软件工程课堂六(《飘》单词统计)
    软件工程第八周总结
    跨域
  • 原文地址:https://www.cnblogs.com/Nepenthe8/p/15969143.html
Copyright © 2020-2023  润新知