P8078 [WC2022] 秃子酋长 题解
考场上只写了一个 \(O(n\sqrt n \log n)\) 的暴力。
然后以为是什么二次离线之类的黑科技,压根没往回滚想。
赛后一提醒立马想到了。
个人觉得想到了还是比较好写的。
思路
考虑离线莫队。
暴力做法是用莫队维护左右端点,加入和删除都用一个 \(\text{set}\) 或者平衡树去维护,对于答案的修改直接在平衡树中查询。
时间复杂度:\(O(n\sqrt n \log n)\)
期望:\(\text{40pts}\)
我们发现这个东西可以用回滚来优化。
由于在暴力做法中平衡树的作用仅仅只是查询前驱和后继,我们知道,对于一个有序的链表,它可以 \(O(1)\) 实现这个操作。
所以考虑不增加莫队。
维护一个链表,一个链头,一个链尾。
首先预处理出左右端点为 \(\text{1}\) 和 \(\text{n}\) 的有序链表,并且计算答案。
接着将询问按左端点所在的块为第一关键字排序。
对于每一个块中的询问。
-
将左端点删除至此时块的左端点,保存此时的链表。
-
将由于在同一块内的询问,右端点是递减的,所以可以直接删除。
-
将此时的链头,链尾,答案保存,方便等一下恢复左端点。
-
将左端点删除至询问的左端点,统计答案。
-
由于在挪动完左端点后没有进行其他的操作,所以可以直接恢复,并用之前保存的链头,链尾,答案更新。
-
循环处理询问,当此时的块内询问处理完之后,将保存的链表复制到此时的链表上,左端点再次回到此时块内的左端点,右端点变为 \(\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;
}