2018.1.25 T3 数列问题

在学校住了两天感觉挺好的,但因为Goseqh回家了,我也只好回家了。

我最近写程序可能是状态比较差,漏洞百出,各种诡异的错误挑不错来。调试了好长时间这道题才调过。

【题面】

题目描述

更正,当k=0时,满足条件的数不能为这个数自身,即 ij i ≠ j

输入描述

输出描述

样例输入

样例输出和数据范围

【思路】

离线 + 线段树。

我们可以出一个数组 d d di=min{j:|aiaj|=k,j>i},若不存在满足条件的j,令 di=+ d i = + ∞ 。可以从右到左扫描当前序列,用map记录每种数值最后一次出现(即下标最靠前一次出现)时的位置。对于每一个位置 i i ,有di=min(LastExist(ai+k),LastExist(aik))i。然后再更新: LastExist(ai)=i L a s t E x i s t ( a i ) = i

考虑假如每次询问的x都等于1,那有什么巧妙的解决问题的办法。

我们不难发现所有的 i<j,|aiaj|=k i < j , | a i − a j | = k (这里的 j j 实际上是di,因为 di d i 一定是i右面最近的那个j),只要i和j都在1~y的范围内, ji j − i 就会称为可能的答案。我们不妨记录一个数组 b b ,令bj=ji(若不存在i, bj=+ b j = + ∞ ),这样的话我们只需要查询b数组在1~y上的最小值就可以解决问题。因为右边界比左边界更不容易进入到1~y的范围里,而只有这两个边界,同时进入范围内才可能对答案造成贡献。

显然,如果算出的最小值为 + + ∞ 那么就说明没有满足条件的i和j,输出-1就可以了。

按照相似的思路,我们可以把所有询问读入,按照x值(即左端点值)从大到小排序。

用线段树维护一个支持单点修改,查询区间最小值的,长度为n的序列 {bi} { b i } 。记录一个L值,L值从右向左推移。L值每到达一个位置 i i ,就让bdi=min(bdi,dii)。按照预先排好的顺序考虑所有询问,每一次把L值向左推到当前询问的x的位置,查询 min{bx,bx+1,...,by} min { b x , b x + 1 , . . . , b y } 即为答案。(L就相当于上上一段中的“1”,是一个固定了的起始位置)


【代码】

其实还是蛮好写的,但是我状态不太好,写错了好多地方,好久才调试出来。

#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;

#define maxn (100000 + 10)
#define inf  (0x7f7f7f7f)

int lch[maxn], rch[maxn], msg[maxn], ncnt;//min
int lst[maxn], rst[maxn];
void build(int root, int l, int r){
    lst[root] = l; rst[root] = r;
    if(l == r){
        lch[root] = rch[root] = -1; //no son
        msg[root] = inf;
    }else{
        int mid = (l+r)/2;
        int nl = ++ncnt, nr = ++ncnt;
        build(nl, l, mid);
        build(nr, mid+1, r);
        msg[root] = min(msg[nl], msg[nr]);
        lch[root] = nl; rch[root] = nr;
    }
}

int query(int root, int l, int r){
    if(lst[root]>r || rst[root]<l) return inf;
    if(l<=lst[root] && rst[root]<=r) return msg[root];
    int nl = lch[root], nr = rch[root];
    return min(query(nl, l, r),query(nr, l, r));
}

void change(int root, int pos, int val){
    if(lst[root] == rst[root]){
        msg[root] = min(msg[root], val);
    }else{
        int mid = (lst[root] + rst[root])/2;
        if(pos <= mid){
            change(lch[root], pos, val);
        }else change(rch[root], pos, val);
        msg[root] = min(msg[lch[root]], msg[rch[root]]);
    }
}

int a[maxn], d[maxn], n, k;
map<int, int> lastp;

#define POS(X) ((X) == 0 ? inf : (X))
#define ANS(X) ((X) >= inf ? -1 : (X))

struct que{
    int l, r, t;
}qs[maxn]; int ans[maxn];

inline bool cmp(que a, que b){
    return a.l > b.l;
}

int main(){
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    scanf("%d%d", &n, &k);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    for(int i=n; i>=1; i--){
        d[i] = min(POS(lastp[a[i] + k]), POS(lastp[a[i] - k]));
        lastp[a[i]] = i;
    }
    int q; scanf("%d", &q);
    for(int i=1; i<=q; i++) scanf("%d%d", &qs[i].l, &qs[i].r), qs[i].t=i;
    int L = n+1, root = ++ncnt;
    build(root, 1, n);
    sort(qs+1, qs+q+1, cmp);
    for(int i=1; i<=q; i++){
        while(L > qs[i].l){
            L--;
            if(d[L] != inf) change(root, d[L], d[L]-L);
        }
        ans[qs[i].t] = ANS(query(root, qs[i].l, qs[i].r));
    }
    for(int i=1; i<=q; i++){
        printf("%d\n", ans[i]);
    }
    return 0;
}

/*
7 1
2 1 3 8 4 2 1
4
1 5
4 6
4 7
2 5
*/

其中有一个比较诡异的错误:我把msg[root]写成了msg[maxn]导致它改写了无辜的ncnt,使ncnt变成了inf。。

还有就是推动L时,好几次把好几个L都写成了i,看了半天才看出来。

2018.1.25

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值