peiwenjun's blog 没有知识的荒原

P8078 [WC2022] 秃子酋长 题解

题目描述

给定一个长为 \(n\) 的排列 \(a\)\(q\) 次询问,每次询问 \(a[l\sim r]\) 排序后相邻位置的差在原序列中下标差的绝对值之和。

数据范围

  • \(1\le n,q\le 5\cdot 10^5\)

时间限制 \(\texttt{5s}\) ,空间限制 \(\texttt{512MB}\)

分析

显然莫队套 set 可以做到 \(\mathcal O(q\sqrt n\log n)\) ,期望得分 \(50pts\)

我们需要 \(\mathcal O(1)\) 查询前驱后继的数据结构,刚好链表符合要求。

但是暂时还用不了链表,因为我们并不知道在哪里插入和删除。

逆排列上考虑这个问题,每个点 \(a_i\) 有权值 \(i\)\(q\) 次询问,每次询问给定若干散点,求相邻散点权值差的绝对值之和。

链表删除是容易的,但是不支持插入。

那就回滚莫队。具体地,初始化 \(l\) 指针为块的左端点,块内询问按右端点降序排序。

每次先把 \(r\) 指针移动到正确的位置上,然后向右滚动 \(l\) ,最后撤销滚动 \(l\) 造成的影响。

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

卡常提示:给 del 函数加 inline 的优化效果非常明显,下面这份代码 \(\texttt{>5.2s}\to\texttt{4.4s}\)

#include<bits/stdc++.h>
#define ll long long
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
using namespace std;
const int B=900,maxn=5e5+5;
int m,n;
int p[maxn],q[maxn],bel[maxn];
int pos[maxn],pre[maxn],nxt[maxn];
ll sum,tmp,res[maxn];
char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
struct query
{
    int l,r,id;
}f[maxn];
int read()
{
    int q=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=10*q+ch-'0',ch=getchar();
    return q;
}
void write(ll x)
{
    static int top=0,st[20];
    do st[++top]=x%10,x/=10;
    while(x);
    while(top) *O++=st[top--]+'0';
    *O++='\n';
}
bool cmp(query p,query q)
{
    if(bel[p.l]!=bel[q.l]) return bel[p.l]<bel[q.l];
    return p.r>q.r;
}
inline void del(int x)
{
    if(pre[x]) sum-=abs(q[x]-q[pre[x]]);
    if(nxt[x]) sum-=abs(q[x]-q[nxt[x]]);
    if(pre[x]&&nxt[x]) sum+=abs(q[pre[x]]-q[nxt[x]]);
    pre[nxt[x]]=pre[x],nxt[pre[x]]=nxt[x];
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++) p[i]=read(),q[p[i]]=i,bel[i]=(i-1)/B+1;
    for(int i=1;i<n;i++) tmp+=abs(q[i+1]-q[i]);
    for(int i=1;i<=bel[n];i++) pos[i]=(i-1)*B+1;
    for(int i=1;i<=m;i++) f[i].l=read(),f[i].r=read(),f[i].id=i;
    sort(f+1,f+m+1,cmp);
    for(int i=1,l=1,r=0;i<=m;i++)
    {
        if(bel[f[i].l]!=bel[f[i-1].l])
        {
            iota(pre+1,pre+n+1,0),iota(nxt+1,nxt+n,2);
            l=1,r=n,sum=tmp;
            while(l<pos[bel[f[i].l]]) del(p[l++]);
        }
        while(r>f[i].r) del(p[r--]);
        ll cur=sum;
        while(l<f[i].l) del(p[l++]);
        res[f[i].id]=sum,sum=cur;
        while(l>pos[bel[f[i].l]])
        {
            int x=p[--l];
            pre[nxt[x]]=x,nxt[pre[x]]=x;
        }
    }
    for(int i=1;i<=m;i++) write(res[i]);
    fwrite(obuf,1,O-obuf,stdout),O=obuf,fflush(stdout);
    return 0;
}

posted on 2022-05-12 23:38  peiwenjun  阅读(26)  评论(0)    收藏  举报

导航