2019 南京网络赛 G Greedy Sequence(主席树)

链接


思路:

从小枚举i,当i作为序列的第一个的时候,记i在原数组中的下标为idx,在[idx-k,idx+k](边界需要判断下)的范围找到比i小的数中最大的那个记为maxn,那么i的答案就是maxn的答案+1,如果没有maxn,那么答案就是1。查找maxn我是直接使用暴力主席树找小于等于i的个数k,然后查找区间第k-1的数,跑了近4000ms,卡过。


参考代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;

int a[N];
int root[N],tot;
int lson[N*25],rson[N*25],sz[N*25];
void update(int last,int cur,int l,int r,int k){
    lson[cur]=lson[last];
    rson[cur]=rson[last];
    sz[cur]=sz[last]+1;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(k<=mid)update(lson[last],lson[cur]=++tot,l,mid,k);
    else update(rson[last],rson[cur]=++tot,mid+1,r,k);
}

int query(int last,int cur,int l,int r,int k){//查找小于等于k的数量
    if(k<l)return 0;
    if(k>=r){
        return sz[cur]-sz[last];
    }
    int mid=(l+r)>>1;
    int sum=0;
    sum+=query(lson[last],lson[cur],l,mid,k);
    sum+=query(rson[last],rson[cur],mid+1,r,k);
    return sum;
}

int query2(int last,int cur,int L,int R,int k)//区间第k小
{
//    printf("%d %d %d\n",L,R,k);
    if(L==R)return L;
    int mid=(L+R)>>1;
    int lll=sz[lson[cur]]-sz[lson[last]];
//    printf("sz:%d\n",lll);
    if(lll>=k)return query2(lson[last],lson[cur],L,mid,k);
    else return query2(rson[last],rson[cur],mid+1,R,k-lll);
}

int ans[N];
int pos[N];

int main(){
    int t;
    scanf("%d",&t);
    for(int ca=1;ca<=t;ca++){
        int n,k;
        scanf("%d%d",&n,&k);
        tot=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            pos[a[i]]=i;
            update(root[i-1],root[i]=++tot,1,n,a[i]);
        }
        for(int i=1;i<=n;i++){
            int l=max(1,pos[i]-k);
            int r=min(n,pos[i]+k);
            int ss=query(root[l-1],root[r],1,n,i);
            if(ss==1){
                ans[i]=1;
            }
            else{
                int p=query2(root[l-1],root[r],1,n,ss-1);
                ans[i]=ans[p]+1;
            }
        }
        for(int i=1;i<=n;i++){
            printf("%d%c",ans[i],i==n?'\n':' ');
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值