zoukankan      html  css  js  c++  java
  • luogu题解 CF522D Closest Equals

    写在前面

    注:因为变量名中有下划线,所以有些地方使用的是代码块而没有使用 \(\text{LateX}\) 渲染,恳请管理大大原谅。

    转了一圈题解发现没有用莫队的?

    唯一一个涉及到的貌似还被卡了?

    那么我就来一发回滚莫队吧。

    题面:CF522D Closest Equals

    Solution

    一开始把题目读错了,以为求的是一段区间内最远的两个相同的数的距离,

    这不就回滚莫队板子嘛

    调半天发现是求最近的两个相同的数的距离,想想怎么用莫队维护。

    跟着回滚莫队的做法,先进行离散化、分块、排序这一类基本操作。

    然后每次轮到一个新块时,我们不是要移动两端点到块的右端嘛。

    我们要查询的区间因此就分成了两块,暂且称作左块和右块。

    题目要求是求最近的,那么只需统计相邻的两个相同的数的贡献。

    开四个数组 cntr[x], cntr_pre[x], cntl_t[x], cntl_pre_t[x],分别表示一个数 \(x\) 在右块的最小的位置,\(x\) 在右块中上一次出现的位置,\(x\) 在左块中的最大位置,\(x\) 在左块中上一次出现的位置。

    画成图大体是这个样子:

    假设图中标记的都是一个数:

    • 如果在左块中又枚举到一个数 \(pos_1\),它对答案的贡献显然是 cntl_pre_t - pos1
    • 如果在右块中又枚举到一个数 \(pos_2\),它对答案的贡献显然是
      pos2 - cntr_pre
    • 因为左块和右块之间可能也会有贡献,所以还需要统计 cntr - cntl_t

    一些小细节:

    • 注意没有相同的数时要输出 \(-1\)
    • 在左块或者右块中统计答案时,如果其中某一个值不存在就不要统计答案了。

    Code

    /*
    Work by: Suzt_ilymics
    Knowledge: ??
    Time: O(??)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 5e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    struct Ques{
        int l, r, bh;
    }q[MAXN];
    
    int n, m; 
    int L = 1, R = n, preL = 0, Ans, Res, _L;
    int a[MAXN], date[MAXN], date_num = 0;
    int lef[MAXN], rig[MAXN], bel[MAXN];
    int cntr[MAXN], cntr_pre[MAXN], cntl_t[MAXN], cntl_pre_t[MAXN];
    int ans[MAXN];
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    bool cmp(Ques x, Ques y) { return bel[x.l] == bel[y.l] ? x.r < y.r : x.l < y.l; } //按块排序 
    
    void Init() {
        n = read(), m = read();
        for(int i = 1; i <= n; ++i) a[i] = date[i] = read();
        sort(date + 1, date + n + 1); date[0] = -INF; //离散化 
        for(int i = 1; i <= n; ++i) if(date[i] != date[i - 1]) date[++date_num] = date[i];
        for(int i = 1; i <= n; ++i) a[i] = lower_bound(date + 1, date + date_num + 1, a[i]) - date;
        for(int i = 1; i <= date_num; ++i) cntr[i] = cntr_pre[i] = ans[i] = INF; 
        for(int i = date_num + 1; i <= m; ++i) ans[i] = INF; //初始化为极大值 
        
        int len = sqrt(n + 0.5);//分块 
        int block_num = n / len;
        for(int i = 1; i <= block_num; ++i) lef[i] = rig[i - 1] + 1, rig[i] = rig[i - 1] + len; 
        if(rig[block_num] < n) {
            ++ block_num;
            lef[block_num] = rig[block_num - 1] + 1;
            rig[block_num] = n;
        }
        for(int i = 1; i <= block_num; ++i) 
            for(int j = lef[i]; j <= rig[i]; ++j) 
                bel[j] = i;
    }
    
    void Add(int pos, int &ans_) {
        int x = a[pos];
        cntr[x] = min(cntr[x], pos); //更新右块中x的最小的位置
        if(cntr_pre[x] != INF) ans_ = min(ans_, pos - cntr_pre[x]); // 只有x在右块中出现过才统计答案
        cntr_pre[x] = pos; // 更新
    }
    void Add_tem(int pos, int &ans_, int type) {
        int x = a[pos];
        cntl_t[x] = max(cntl_t[x], pos); // 更新左块中x的最大位置
        if(cntl_pre_t[x]) ans_ = min(ans_, cntl_pre_t[x] - pos); //只有x在左块中出现过才统计答案
        cntl_pre_t[x] = pos; //更新
        if(cntr[x] != INF && cntl_t[x] && type) ans_ = min(ans_, cntr[x] - cntl_t[x]); //统计左右块之间的贡献
    }
    
    void Del(int pos) { cntr[a[pos]] = cntr_pre[a[pos]] = INF; }
    void Del_tem(int pos) { cntl_t[a[pos]] = cntl_pre_t[a[pos]] = 0; }
    
    int main()
    {
        Init();
        for(int i = 1; i <= m; ++i) q[i].l = read(), q[i].r = read(), q[i].bh = i;
        sort(q + 1, q + m + 1, cmp);
        for(int i = 1; i <= m; ++i) { //回滚莫队基操?
            if(preL != bel[q[i].l]) {
                while(R > rig[bel[q[i].l]]) Del(R--);
                while(L <= rig[bel[q[i].l]]) Del(L++);
                Ans = INF, R = rig[bel[q[i].l]], preL = bel[q[i].l];
            }
            if(bel[q[i].l] == bel[q[i].r]) {
                for(int j = q[i].r; j >= q[i].l; --j) Add_tem(j, ans[q[i].bh], 0); //倒序枚举方便统计答案 
                for(int j = q[i].r; j >= q[i].l; --j) Del_tem(j);
            } else {
                while(R < q[i].r) Add(++R, Ans);
                _L = L, Res = Ans;
                while(_L > q[i].l) Add_tem(--_L, Res, 1);
                ans[q[i].bh] = Res;
                while(_L < L) Del_tem(_L++);
            }
        }
        for(int i = 1; i <= m; ++i) printf("%d\n", ans[i] == INF ? -1 : ans[i]);
        return 0;
    }
    
  • 相关阅读:
    在Python中调用C++模块
    [转载]科学家真懂统计学吗?
    [原]基因组变异检测概述
    [原]测序量估计
    软考之路—解答数据流图
    软考之路-搞定ER图到关系模式
    软考之路-网络攻击:主动攻击和被动攻击
    软考之路-计算机病毒
    初识ADO.net-访问数据的两种方式
    vb.net小试三层架构
  • 原文地址:https://www.cnblogs.com/Silymtics/p/14588074.html
Copyright © 2011-2022 走看看