bzoj4358-permu

给出一个\(n\)的排列,\(m\)次询问\((l,r)\),求其中最长值域连续段的长度。

\(n,m\le 5\times 10^4\)

分析

这个问题看似很难做,但它不要求在线,所以可以考虑离线然后使用莫队算法。

记录值域上每个位置左右两边各有多少个连续存在于当前区间的位置。在中间插入一个位置的时候,修改左右两边就行了。但如果修改左右两边的全部位置,时间明显无保证,又注意到如果只有添加的话只用修改这个连续区间的左右端点,我们考虑如何进行无删除的莫队。

不妨设\(n,m\) 同阶。依然把询问区间分为\(B\)块,对询问进行排序,左端点所在块为第一关键字,右端点为第二关键字。对于块内询问,我们暴力处理它,一次为\(O(B)\) 。对于\(l\)在一个块中,\(r\)不在这个块中的情况,我们可以发现\(r\)是递增的,所以可以不用删除。那\(l\)怎么办呢?我们就让它每次都从块尾往左移动到需要的位置,再移回来——由于一次只用改最多两个地方,我们可以记录每一次\(l\)移动导致的修改,回退一下就可以了。这种情况下右端点的移动次数为\(O(\frac{n^2}{B})\),左端点的移动次数为\(O(nB)\),总复杂度为\(O(2nB+\frac{n^2}{B})\),取\(B=\sqrt \frac{n}{2}\) 即可取到最优 \(O(2n\sqrt {2n})\)

这题还有一个奇妙做法,把询问看成一个点,那么位置为\(i\)的数只会被以\((i,i)\)为右下角的矩形中的询问包含。于是我们用把\(1\cdots n\) 一个个处理,用KD-Tree来维护历史最大值即可。复杂度为\(O(n\sqrt n)\)

代码

这是莫队的做法。

#include<cstdio>
#include<cctype>
#include<cstring>
#include<cmath>
#include<algorithm>
#define M(x) memset(x,0,(sizeof x[0])*(n+1))
using namespace std;
inline char nchar() {
    const int bufl=1<<20;
    static char buf[bufl],*a,*b;
    return a==b && (b=(a=buf)+fread(buf,1,bufl,stdin),a==b)?EOF:*a++;
}
inline int read() {
    int x=0,f=1;
    char c=nchar();
    for (;!isdigit(c);c=nchar()) if (c=='-') f=-1;
    for (;isdigit(c);c=nchar()) x=x*10+c-'0';
    return x*f;
}
inline void write(int x) {
    if (!x) puts("0"); else {
        static char s[13];
        int tot=0;
        for (;x;x/=10) s[++tot]='0'+x%10;
        while (tot) putchar(s[tot--]);
        puts("");
    }
}
const int maxn=5e4+1;
int n,m,size,a[maxn],block[maxn],eb[maxn],ans[maxn],theans;
int lef[maxn],rig[maxn];
void Max(int &x,int y) {x=max(x,y);}
struct query {
    int l,r,id;
    inline bool operator < (const query &a) const {return block[l]==block[a.l]?r<a.r:block[l]<block[a.l];}
} b[maxn];
void inc(int p,int &theans) {
    int x=a[p];
    int l=lef[x-1],r=rig[x+1];
    if (l && r) Max(theans,rig[x-l]=lef[x+r]=l+r+1); else 
    if (l && !r) Max(theans,lef[x]=++rig[x-l]),Max(theans,rig[x]=1); else 
    if (!l && r) Max(theans,rig[x]=++lef[x+r]),Max(theans,lef[x]=1); else 
    Max(theans,rig[x]=lef[x]=1);
}
void brute(query &c) {
    theans=0;
    int &l=c.l,&r=c.r,&id=c.id;
    for (int i=l;i<=r;++i) inc(i,theans);
    ans[id]=theans;
    for (int i=l;i<=r;++i) lef[a[i]]=rig[a[i]]=0;
}
int gol(int p,int dist,int theans) {
    int x=a[p];
    int l=lef[x-1],r=rig[x+1];
    int bef[]={lef[x],rig[x],lef[x+r],rig[x-l]};
    inc(p,theans);
    int ret;
    ret=(p==dist?theans:gol(p-1,dist,theans));
    lef[x]=bef[0],rig[x]=bef[1],lef[x+r]=bef[2],rig[x-l]=bef[3];
    return ret;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
#endif
    n=read(),m=read();
    int size=max(1,(int)sqrt((double)n/2));
    for (int i=1;i<=n;++i) block[i]=(i-1)/size+1;
    for (int i=1;i<=n;++i) if (block[i]!=block[i+1]) eb[block[i]]=i;
    for (int i=1;i<=n;++i) a[i]=read();
    for (int i=1;i<=m;++i) {
        int l=read(),r=read();
        b[i]=(query){l,r,i};
    }
    sort(b+1,b+m+1);
    for (int i=1,j;i<=m;i=j+1) {
        M(lef),M(rig);
        int wh=block[b[i].l];
        for (j=i-1;j<m && block[b[j+1].l]==wh && block[b[j+1].r]==wh;++j);
        for (int k=i;k<=j;++k) brute(b[k]);
        int st=j+1;
        for (;j<m && block[b[j+1].l]==wh;++j);
        int ew=eb[wh],nowr=ew;
        theans=0;
        for (int k=st;k<=j;++k) {
            int &l=b[k].l,&r=b[k].r,&id=b[k].id;
            while (nowr<r) inc(++nowr,theans);
            ans[id]=gol(ew,l,theans);
        }
    }
    for_each(ans+1,ans+m+1,write);
    return 0;
}

转载于:https://www.cnblogs.com/owenyu/p/7218518.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值