区间第k小 分块

来源:互联网 发布:网络婚恋诈骗广东 编辑:程序博客网 时间:2024/06/06 02:34

题意

这里写图片描述
这里写图片描述
强制在线

分析

首先考虑离线做法。可以莫队+分块做。
莫队在指针动的同时,维护一个权值分块,然后每次O(1)修改,查询的时候可以O(sqrt(n))查询。这样复杂度就是O(sqrt(n))。
现在加上了强制在线,我们可以把莫队改成分块。
预处理g[i,j,k]表示序列分块中第k块到第j块有多少个数字在权值分块的第k块中,s[i,j]表示前i块中j出现的次数。
那么我们可以通过s和g来得到询问区间的权值分块数组,然后每次O(sqrt(n))询问即可。

yjq大爷告诉我们,像这种复杂度为log^2的题目,可以尝试着用分块来做,复杂度并不会相差太多。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>using namespace std;const int N=100005;const int M=325;int n,m,ty,w,bel[N],sta[M],end[N],s[M][N],g[M][M][M],block,t[N],sum[M],a[N];int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}void prework(){    block=sqrt(n);    for (int i=1;i<=n;i++)    {        bel[i]=(i+block-1)/block;        if (!sta[bel[i]]) sta[bel[i]]=i;        end[bel[i]]=i;    }    for (int i=1;i<=bel[n];i++)    {        for (int j=sta[i];j<=end[i];j++) s[i][a[j]]++;        for (int j=1;j<=n;j++) s[i][j]+=s[i-1][j];    }    for (int i=1;i<=bel[n];i++)    {        for (int j=i;j<=bel[n];j++)        {            for (int k=1;k<=bel[n];k++) g[i][j][k]=g[i][j-1][k];            for (int k=sta[j];k<=end[j];k++)            {                t[a[k]]++;                if (t[a[k]]==w+1) g[i][j][bel[a[k]]]-=w;                else if (t[a[k]]<=w) g[i][j][bel[a[k]]]++;            }        }        for (int j=sta[i];j<=n;j++) t[a[j]]--;    }}int solve(int l,int r,int k){    memset(sum,0,sizeof(sum));    if (bel[l]!=bel[r])    {        for (int i=1;i<=bel[n];i++) sum[i]=g[bel[l]+1][bel[r]-1][i];        for (int i=l;i<=end[bel[l]];i++)        {            if (!t[a[i]]) t[a[i]]=s[bel[r]-1][a[i]]-s[bel[l]][a[i]];            t[a[i]]++;            if (t[a[i]]==w+1) sum[bel[a[i]]]-=w;            else if (t[a[i]]<=w) sum[bel[a[i]]]++;        }        for (int i=sta[bel[r]];i<=r;i++)        {            if (!t[a[i]]) t[a[i]]=s[bel[r]-1][a[i]]-s[bel[l]][a[i]];            t[a[i]]++;            if (t[a[i]]==w+1) sum[bel[a[i]]]-=w;            else if (t[a[i]]<=w) sum[bel[a[i]]]++;        }    }    else    {        for (int i=l;i<=r;i++)        {            t[a[i]]++;            if (t[a[i]]==w+1) sum[bel[a[i]]]-=w;            else if (t[a[i]]<=w) sum[bel[a[i]]]++;        }    }    for (int i=1;i<=bel[n];i++)        if (sum[i]<k) k-=sum[i];        else        {            for (int j=sta[i];j<=end[i];j++)            {                if (!t[j]) t[j]=max(s[bel[r]-1][j]-s[bel[l]][j],0);                if (t[j]>w) continue;                if (t[j]<k) k-=t[j];                else                {                    for (int k=sta[i];k<=end[i];k++) t[k]=0;                    for (int k=l;k<=end[bel[l]];k++) t[a[k]]=0;                    for (int k=sta[bel[r]];k<=r;k++) t[a[k]]=0;                    return j;                }            }            break;        }    for (int k=l;k<=end[bel[l]];k++) t[a[k]]=0;    for (int k=sta[bel[r]];k<=r;k++) t[a[k]]=0;    return n+1;}int main(){    freopen("kth.in","r",stdin);freopen("kth.out","w",stdout);    n=read();w=read();m=read();ty=read();    for (int i=1;i<=n;i++) a[i]=read()+1;    prework();    int ans=0;    while (m--)    {        int l=read()^(ans*ty),r=read()^(ans*ty),k=read()^(ans*ty);        printf("%d\n",ans=solve(l,r,k)-1);    }    return 0;}
原创粉丝点击