【JZOJ5260】【GDOI2018模拟8.12】区间第k小(分块)

Description

【JZOJ5260】【GDOI2018模拟8.12】区间第k小(分块)_第1张图片

Solution

首先这题离线的话是可以莫队套分块的,但是要求强制在线。那么我们只能考虑把莫队换成分块。
首先处理一个关于值域的块,然后求一个数组f[i,j,k]表示从下标第i块到第j块中满足在值域块是第k块,且这些数字出现次数小于等于w的有多少个,这个枚举两个端点,然后扫过去,用桶记录一下就好了。
然后再处理一个数组g[i,j]表示前i块中值为j的有多少个。
那么现在我们就可以考虑先把答案用f确定是哪一个块,然后再用g确定是那一个数字。
对于询问[l,r]先把两边下标多余的部分(就是不是整段都在块中)与中间的合并,具体就是统计其中数字出现的次数,然后与中间部分出现的次数相加,如果大于w,那么求答案的时候就不能考虑(就是把f减掉),否则就把f加上两边出现的次数,最后做完再加(减)回
来。然后先找到确定的块,然后枚举块中的数字确定是哪一个就好了。
复杂度O(n n )

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=1e5+7;
int i,j,k,l,t,n,m,ans,type,w,q,r,o,l1,r1,yi,er,x;
int f[350][350][350],g[350][maxn],c[maxn],d[maxn];
int a[maxn],e1,e2,shu[maxn],b[maxn],w1,w2;
bool bz[maxn];
int main(){
    freopen("kth.in","r",stdin);
    freopen("kth.out","w",stdout);
    scanf("%d%d%d%d",&n,&w,&q,&type);e1=sqrt(n);
    fo(i,1,n)scanf("%d",&a[i]),shu[i]=(i-1)/e1+1;w1=shu[n];
    e2=sqrt(maxn-7);fo(i,0,maxn-7)b[i]=i/e2+1;w2=b[maxn-7];
    fo(i,1,n)g[shu[i]][a[i]]++;
    fo(j,0,maxn-7)fo(i,1,w1)g[i][j]+=g[i-1][j];
    fo(i,1,w1){
        fo(j,i,w1){
            fo(l,(j-1)*e1+1,j*e1){
                if(l>n)break;
                if(d[a[l]]<0)continue;
                d[a[l]]++;
                if(d[a[l]]>w)f[i][j][b[a[l]]]-=d[a[l]]-1,d[a[l]]=-1;
                else f[i][j][b[a[l]]]++;
            }
        }
        memset(d,0,sizeof(d));
    }
    fo(k,1,w2)fo(i,1,w1)fo(j,i+1,w1)f[i][j][k]+=f[i][j-1][k];
    while(q--){
        scanf("%d%d%d",&l,&r,&k);l^=(ans*type),r^=(ans*type),k^=(ans*type);
        l1=shu[l]*e1+1;r1=(shu[r]-1)*e1;yi=shu[l1],er=shu[r1];o=0;t=0;c[0]=0;
        if(l11)d[a[i]]++,c[++c[0]]=a[i];fo(i,r1+1,r)d[a[i]]++,c[++c[0]]=a[i];
            fo(i,1,c[0])if(!bz[c[i]]){
                x=c[i];bz[x]=1;
                if(g[er][x]-g[yi-1][x]<=w&&g[er][x]-g[yi-1][x]+d[x]>w)f[yi][er][b[x]]-=g[er][x]-g[yi-1][x];
                else if(g[er][x]-g[yi-1][x]+d[x]<=w)f[yi][er][b[x]]+=d[x];
            }
        }
        else{
            yi=er=w2+2;
            fo(i,l,r)d[a[i]]++,c[++c[0]]=a[i];
            fo(i,l,r)if(!bz[a[i]]){
                bz[a[i]]=1;
                if(d[a[i]]<=w)f[yi][er][b[a[i]]]+=d[a[i]];
            }
        }
        fo(i,1,w2){
            o+=f[yi][er][i];
            if(o>=k){t=i;o-=f[yi][er][i];break;}
        }
        if(t){
            fo(i,(t-1)*e2,t*e2-1){
                if(i>maxn-7)break;
                if(i==154){
                    ans=ans;
                }
                if(g[er][i]-g[yi-1][i]+d[i]){
                    ans=ans;
                }
                if(g[er][i]-g[yi-1][i]+d[i]>w)continue;
                o+=g[er][i]-g[yi-1][i]+d[i];
                if(o>=k){
                    ans=i;
                    break;
                }
            }
        }
        if(oprintf("%d\n",ans);
        fo(i,1,c[0]){
            if(bz[c[i]]){
                x=c[i];bz[x]=0;
                if(g[er][x]-g[yi-1][x]<=w&&g[er][x]-g[yi-1][x]+d[x]>w)f[yi][er][b[x]]+=g[er][x]-g[yi-1][x];
                else if(g[er][x]-g[yi-1][x]+d[x]<=w)f[yi][er][b[x]]-=d[x];
                d[x]=0;
            }
        }
    }
}

你可能感兴趣的:(区间第k小,分块,莫队,根号算法,GDOI,省选,分块大法,暴搜)