[JZOJ5296] Sequence

本文介绍了一种高效的算法策略,用于解决包含二维查询的问题。通过使用整体二分法和树状数组来优化查询过程,实现了时间复杂度为O(nlogn+nlog²n)的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

这里写图片描述
这里写图片描述

分析

这道题这么多关键字,似乎很难搞,先想一想。
30分暴力很容易嘛,就是莫队之后上KD树二位查询
100分呢?
考虑到最后一维的经典做法是主席树上二分,我们考虑整体二分来做。
首先为了方便,我们先把询问的x,y处理一下,把排名在[x,y]之间变成第一关键字在[x’,y’]之间。
考虑整体二分。先考虑一开始对询问区间[1,m]怎么做。我们有二分的答案mid=(1+n)/2,考虑先排序去掉一维的限制,我们把询问拆成l-1,r两个询问,和n个元素扔到一起,按照序列下标排序,用数据结构维护。那么现在询问就变成了,查询[x,y]之间有几个元素第二关键字小于等于mid。由于整体二分,第二关键字大于mid的元素不用管,那么先把他扔出处理队列,不管他;小于等于mid的元素在当前局面下性质是一样的,所以第二关键字也不用管了。现在只要处理单点插入1,区间查询总和这一问题,那么可以用树状数组做,常数小,log2下时间有保证。

好了处理完之后,我们要给询问和元素分类,然后分治下去了。
考虑一个询问,如果区间查询出来的结果cnt大于它的k,把他扔到mid+1~r的分治区间,并且把k-=cnt;不然就直接扔进1~mid的分治区间,k不动。
对于一个元素,如果它的第二关键字大于mid,他对左边的1~mid的分治区间没有任何贡献,扔到右边;如果第一关键字小于等于mid,他对右边的分治区间已经计算过贡献,即前面的k-=cnt,所以没必要传递到右区间,直接扔进左区间。
这样就做完了,时间复杂度O(nlogn+nlog2n),一个主席树,一个整体二分+树状数组。
60分也很简单,你把树状数组改成线段树就行了
我就是打了线段树然后开了O2

代码

#pragma GCC optimize(2)
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set> 
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=1e5+5,mo=1e9+7,mx=1e9;
int tr[N*4];
struct Query
{
    int l,r,x,y,k,s,id;
}q[N*3],p[N*3];
int tq;
struct rec
{
    int pos,x,y,s,id;
}c[N*3];
int tc;
bool operator <(rec a,rec b)
{
    return a.pos<b.pos||a.pos==b.pos&&a.s<b.s;
}
int n,m,i,j,kan,a[N],b[N],prt[N],tmp,ax,ay,cnt[N];
int ftr[N*8],ls[N*8],rs[N*8],tt,rt[N];
void changed(int ax,int x,int l,int r,int pos)
{
    if (l==r)
    {
        ftr[x]=ftr[ax]+1;
        return ;
    }
    int m=(l+r)/2;
    if (pos<=m) 
    {
        rs[x]=rs[ax];ls[x]=++tt;
        changed(ls[ax],ls[x],l,m,pos);
    }else
    {
        ls[x]=ls[ax];rs[x]=++tt;
        changed(rs[ax],rs[x],m+1,r,pos);
    }
    ftr[x]=ftr[ls[x]]+ftr[rs[x]];
}
int got(int ax,int x,int l,int r,int k)
{
    if (l==r) return l;
    int m=(l+r)/2,temp=ftr[ls[x]]-ftr[ls[ax]];
    if (temp>=k) 
        return got(ls[ax],ls[x],l,m,k);else
        return got(rs[ax],rs[x],m+1,r,k-temp);
}
void predo()
{
    fo(i,1,n)
    {
        rt[i]=++tt;
        changed(rt[i-1],rt[i],1,n,a[i]);
    }
    fo(i,1,m)
    {
        ax=got(rt[q[i].l-1],rt[q[i].r],1,n,q[i].x);
        q[i].x=ax;
        ay=got(rt[q[i].l-1],rt[q[i].r],1,n,q[i].y);
        q[i].y=ay;
    }
}
void change(int x,int l,int r,int pos,int inc)
{
    if (l==r) 
    {
        tr[x]+=inc;
        return ;
    }
    int m=(l+r)/2;
    if (pos<=m) 
        change(x*2,l,m,pos,inc);else
        change(x*2+1,m+1,r,pos,inc);
    tr[x]=tr[x*2]+tr[x*2+1];
}
int get(int x,int l,int r,int i,int j)
{
    if (l==i&&r==j) return tr[x];
    int m=(l+r)/2;
    if (j<=m) 
        return get(x*2,l,m,i,j);else
    if (m<i)
        return get(x*2+1,m+1,r,i,j);else
        return get(x*2,l,m,i,m)+get(x*2+1,m+1,r,m+1,j);
}
void solve(int sig,Query *que,int st,int en,int l,int r)
{
    if (st>en) return ;
    if (l==r)
    {
        fo(i,st,en) if (que[i].s)
            prt[que[i].id]=l;
        return ;
    }
    int mid=(l+r)/2,i;
    tc=0;
    fo(i,st,en)
    {
        if (!que[i].s&&que[i].y<=mid) 
        {
            c[++tc].pos=que[i].l;
            c[tc].x=que[i].x;
            c[tc].s=-2;
        }
        if (que[i].s)
        {
            c[++tc].pos=que[i].l-1;
            c[tc].x=que[i].x;
            c[tc].y=que[i].y;
            c[tc].s=-1;
            c[tc].id=i;
            c[++tc].pos=que[i].r;
            c[tc].x=que[i].x;
            c[tc].y=que[i].y;
            c[tc].s=1;
            c[tc].id=i;
        }
        cnt[i]=0;
    }
    sort(c+1,c+1+tc);
    fo(i,1,tc)//process
    {
        if (c[i].s==-2) change(1,1,n,c[i].x,1);
        else 
        {
            tmp=get(1,1,n,c[i].x,c[i].y);
            cnt[c[i].id]+=tmp*c[i].s;
        }
    }
    fo(i,1,tc) if (c[i].s==-2) change(1,1,n,c[i].x,-1);// delete
    int sten=st-1,enst=en+1;
    Query *nq;
    if (sig) nq=p;else nq=q;
    fo(i,st,en)
    {
        if ((!que[i].s&&que[i].y<=mid)||(que[i].s&&cnt[i]>=que[i].k))
        {
            sten++;
            nq[sten]=que[i];
        }else
        {
            enst--;
            nq[enst]=que[i];
            if (que[i].s) nq[enst].k-=cnt[i];
        }
    }
    solve(sig^1,nq,st,sten,l,mid);
    solve(sig^1,nq,enst,en,mid+1,r);
}
int main()
{
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",a+i);
    fo(i,1,n) scanf("%d",b+i);
    scanf("%d\n",&m);
    fo(i,1,m)
    {
        scanf("%d %d %d %d %d",&q[i].l,&q[i].r,&q[i].x,&q[i].y,&q[i].k);
        q[i].s=1;
        q[i].id=i;
    }
    predo();
    tq=m;
    fo(i,1,n)
    {
        tq++;
        q[tq].s=0;
        q[tq].l=q[tq].r=i;
        q[tq].x=a[i];
        q[tq].y=b[i];
    }
    solve(1,q,1,tq,1,n);
    fo(i,1,m) printf("%d\n",prt[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值