【分块】【RMQ+二分】【主席树】oj_64 新年组队

4 篇文章 0 订阅
3 篇文章 0 订阅

题目描述

Problem Description

BNU ACM校队有n名队员,从1到n标号,每名队员根据自身情况拥有一个特征值,其中第i名队员的特征值是a[i]。现在BOSS问了m个问题,每个问题给定[l,r],要求小Q同学马上从标号位于区间[l,r]内的队员中选出两名队员,使得这两名队员的特征值相同,并且不默契度要尽可能小,两名队员的不默契度定义为两名队员标号之差的绝对值。对此小Q同学倍感压力,急需你的帮助。

Input

第一行包含2个整数n、m。

第二行包含n个整数a[1]、a[2]、…、a[n],保证0<=a[i]<2^31。

接下来m行,每行包含2个整数l、r,请注意所给的l、r均是经过加密的,解密方式是l=l xor lastans、r=r xor lastans,其中lastans表示上一次操作的输出结果,初始lastans=0,保证解密后1<=l<=r<=n。

对于30%的数据,1<=n,m<=5000。

对于60%的数据,1<=n,m<=50000。

对于100%的数据,1<=n,m<=500000。

Output

输出m行,每行包含一个整数,表示最小的两名队员的不默契度,如果不能选出满足条件的两名队员,请输出-1。

Sample Input

5 3

1 1 2 5 2

1 5

3 5

-4 -6

Sample Output

1

-1

2

题解

这题用分块和rmq都可以,我打的是分块,分成k块f[i][j]记录[i,j]的最小不默契度,用一个dp转移,f[i][j]=min(min(f[i][j],f[i+1][j]),f[i][j-1])

但是在代码中不能这么转移
代码中第一关键字枚举块之间的长度(即j-i)
第二关键字枚举起点(即i)

转移代码若下

for(int i=1;i<=num;i++)
        for(int j=1;j<=num-i;j++)
            f[j][j+i]=min(min(f[j+1][j+i],f[j][j+i]),f[j][j+i-1]);

但是本题的数据非常水,全部输出1可以得55分。记住f数组初始化时要从0到k+1。


Update(10.6)

今天比赛做到了这道题的原题
一开始没想到做过
怀疑是否可以分块(这是我做的第一道分块题啊QAQ)

怀疑的地方在于
Q:若点i在整块a内,j在两端的块b内,那f【a】【b】怎么更新答案
A:虽然因为是不完整块f【a】【b】更新不了答案,但是我们在暴力两端时就更新答案了,所以答案已被更新过(意思是整块不能更新暴力块,但暴力块可以更新整块)


方法二:RMQ+二分
xyl


方法三:主席树


方法四:线段树
见TMK的代码

AC代码(分块)(update:10.6)

update的内容主要是用了一些C++的库
推荐看一下离散化,没有用结构体
代码更短了

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 500010
using namespace std;
int a[N],b[N],c[N],d[N],e[N],f[710][710],n,m,B,p,q,ans,num;
bool cmp(int p,int q){
    if(a[p]==a[q])return p<q;
    else return a[p]<a[q];
}
int main(){
    freopen("data.txt","r",stdin);
    scanf("%d%d",&n,&m);
    B=sqrt(n);
    num=(n-1)/B+1;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        b[i]=(i-1)/B+1;
        c[i]=i;
    }
    sort(c+1,c+n+1,cmp);
    memset(f,0x7f,sizeof(f));
    for(int i=1;i<n;i++)
        if(a[c[i]]==a[c[i+1]]){
            d[c[i]]=c[i+1];
            e[c[i+1]]=c[i];
            f[b[c[i]]][b[c[i+1]]]=min(f[b[c[i]]][b[c[i+1]]],c[i+1]-c[i]);
        }
    for(int i=1;i<=num;i++)
        for(int j=1;j<=num-i;j++)
            f[j][j+i]=min(min(f[j+1][j+i],f[j][j+i]),f[j][j+i-1]);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&p,&q);
        p=p^ans;
        q=q^ans;
        ans=f[b[p]+1][b[q]-1];
        for(int j=p;j<=b[p]*B;j++)if(d[j]!=0&&d[j]<=q)ans=min(ans,d[j]-j);
        for(int j=(b[q]-1)*B+1;j<=q;j++)if(e[j]!=0&&e[j]>=p)ans=min(ans,j-e[j]);
        if(ans==0x7f7f7f7f)ans=-1;
        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值