Loading

P8078 [WC2022] 秃子酋长 题解

考场上只写了一个 \(O(n\sqrt n \log n)\) 的暴力。

然后以为是什么二次离线之类的黑科技,压根没往回滚想。

赛后一提醒立马想到了。

个人觉得想到了还是比较好写的。

思路

考虑离线莫队。

暴力做法是用莫队维护左右端点,加入和删除都用一个 \(\text{set}\) 或者平衡树去维护,对于答案的修改直接在平衡树中查询。

时间复杂度:\(O(n\sqrt n \log n)\)

期望:\(\text{40pts}\)

我们发现这个东西可以用回滚来优化。

由于在暴力做法中平衡树的作用仅仅只是查询前驱和后继,我们知道,对于一个有序的链表,它可以 \(O(1)\) 实现这个操作。

所以考虑不增加莫队。

维护一个链表,一个链头,一个链尾。

首先预处理出左右端点为 \(\text{1}\)\(\text{n}\) 的有序链表,并且计算答案。

接着将询问按左端点所在的块为第一关键字排序。

对于每一个块中的询问。

  1. 将左端点删除至此时块的左端点,保存此时的链表。

  2. 将由于在同一块内的询问,右端点是递减的,所以可以直接删除。

  3. 将此时的链头,链尾,答案保存,方便等一下恢复左端点。

  4. 将左端点删除至询问的左端点,统计答案。

  5. 由于在挪动完左端点后没有进行其他的操作,所以可以直接恢复,并用之前保存的链头,链尾,答案更新。

  6. 循环处理询问,当此时的块内询问处理完之后,将保存的链表复制到此时的链表上,左端点再次回到此时块内的左端点,右端点变为 \(\text{n}\)

时间复杂度 \(O(n\sqrt n)\)

期望:\(\text{100pts}\)

这个做法虽然跑的不怎么快,但好像不怎么卡常。

我只是 \(\text{cout}\) 被卡 \(\text{TLE}\) 了个点,换成 \(\text{printf}\) 就可以了。

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

const int N = 500010;

int n , m , t1 , w1 , ans , num , len , tou , wei , pos[N] , sum[N];
pair<int , int> a[N];

struct List
{
    int hou , qian , val;
}q[N] , h[N];

struct Modui
{
    int l , r , id;
    inline bool operator<(const Modui &tmp) const
    {
        return pos[l] == pos[tmp.l] ? r > tmp.r : pos[l] < pos[tmp.l];
    }
}que[N];

#define l(x) (x - 1) * len + 1
#define r(x) min(x * len , n)

inline int read()
{
    int asd = 0 , qwe = 1; char zxc;
    while(!isdigit(zxc = getchar())) if(zxc == '-') qwe = -1;
    while(isdigit(zxc)) asd = asd * 10 + zxc - '0' , zxc = getchar();
    return asd * qwe;
}

inline void del(int x)
{
    if(x != tou) ans -= abs(x - q[x].qian);
    if(x != wei) ans -= abs(x - q[x].hou);
    if(x != tou && x != wei) ans += abs(q[x].qian - q[x].hou);
    if(x == tou) tou = q[x].hou;
    if(x == wei) wei = q[x].qian;
    q[q[x].qian].hou = q[x].hou;
    q[q[x].hou].qian = q[x].qian;
}

inline void add(int x)
{
    q[q[x].qian].hou = x;
    q[q[x].hou].qian = x;
}

inline void cp(List *x , List *y)
{
    for(int i = 1;i <= n;i++)
        x[i] = y[i];
}

signed main()
{
    n = read() , m = read() , len = sqrt(n);
    for(int i = 1;i <= n;i++)
        a[i].first = read() , a[i].second = i;
    sort(a + 1 , a + n + 1) , tou = a[1].second , wei = a[n].second;
    for(int i = 1;i <= n;i++)
        q[a[i].second].qian = a[i - 1].second,
        q[a[i].second].hou = a[i + 1].second;
    for(int i = 1;i <= n;i++) 
        pos[i] = (i - 1) / len + 1;
    for(int i = 1;i <= n;i++)
        if(i != tou) ans += abs(i - q[i].qian);
    for(int i = 1;i <= m;i++)
        que[i].l = read() , que[i].r = read() , que[i].id = i;
    sort(que + 1 , que + m + 1);
    for(int j = 1 , k = 1 , l = 1 , r = n;j <= pos[n] && k <= m;j++)
    {
        if(pos[que[k].l] == j)
        {
            while(l < l(j)) del(l++); 
            cp(h , q) , num = ans;
            t1 = tou , w1 = wei;
            while(pos[que[k].l] == j)
            {
                while(r > que[k].r) del(r--);
                int su = ans , tt = tou , ww = wei;
                while(l < que[k].l) del(l++);
                sum[que[k].id] = ans;
                while(l > l(j)) add(--l);
                k++ , ans = su , tou = tt , wei = ww;
            }
            cp(q , h) , ans = num;
            tou = t1 , wei = w1 , r = n;
        }
    }
    for(int i = 1;i <= m;i++) printf("%lld\n" , sum[i]);
    return 0;
}

posted @ 2022-03-25 16:58  JiaY19  阅读(145)  评论(0编辑  收藏  举报