牛客网暑期ACM多校训练营(第一场)J 题解

这题是这场的签到题,每次查询求a[1...l]和a[r...n]有多少种不同的数字
这题做法很多,有***树、莫队、离线树状数组。
***树和莫队都是很多人T了的,包括我们队在内,这里我就讲一下离线树状数组的做法。
首先将原数组扩展为2倍长的,即 a[i+n] = a[i]
对于查询a[1...l]和a[r..n]有多少种不同的数字可以转换为查询 a[r...l+n]有多少种不同的数字
首先考虑维护一个前缀和,pre[i]表示a[1...i]有多少种不同的数字,那么对于a[l...r]的答案就为pre[r] - pre[l-1] + 在a[l...r]和a[1...l-1]同时出现的数字的种类
对于如何求在a[l...r]和a[1...l-1]同时出现的数字的种类,可以考虑使用树状数组维护,树状数组的第i个点表示a[i]是否已经在1...l出现过,对于每个查询只需要查树状数组中l~r的区间和即可
那么我们只要对区间查询进行排序,对于左端每次右移的时候把对应的数的下一个位置加入到数状数组中即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;

int n, q;
int a[N];
int pre[N];     //pre[i]表示数组前i个数有多少种不同的数
bool vis[N];
int last[N];    //用来计算nxt数组用的辅助数组,记录每个数上一次出现的位置
int nxt[N];     //用来维护数组中每个位置的数下一次出现的位置
int ans[N];

struct Q{
    int l, r, id;
    bool operator < (const Q &b) const {    //将查询按照左端点排序
        return this->l < b.l;
    }
}query[N];

int bit[N];
int lowbit(int x){
    return x & -x;
}
void update(int x, int y){
    for(int i = x; i < N; i += lowbit(i)){
        bit[i] += y;
    }
}

int Query(int x){
    int ans = 0;
    for(int i = x; i > 0; i -= lowbit(i)){
        ans += bit[i];
    }
    return ans;
}

int Query(int l, int r){
    return Query(r) - Query(l-1);
}


int main(){
#ifdef local
    freopen("in","r",stdin);
#endif
    while(scanf("%d%d", &n, &q)!=EOF){
        memset(bit, 0, sizeof(bit));
        memset(vis, 0, sizeof(vis));
        memset(nxt, -1, sizeof(nxt));
            memset(last, -1, sizeof(last));
        for(int i = 1; i <= n; ++i){
            scanf("%d", &a[i]);
            a[i+n] = a[i];
        }
        n *= 2;
        pre[0]=0;
        for(int i = 1; i <= n; ++i){
            if(!vis[a[i]]){
                vis[a[i]] = true;
                pre[i] = pre[i-1] + 1;
            }
            else{
                pre[i] = pre[i-1];
            }
            if(~last[a[i]]){
                nxt[last[a[i]]] = i;
            }
            last[a[i]] = i;
        }
        for(int i = 0; i < q; ++i){
            scanf("%d%d", &query[i].l, &query[i].r);
            query[i].l += n/2;
            swap(query[i].l, query[i].r);
            query[i].id = i;
        }
        sort(query, query+q);
        int nowl = 1;
        for(int i = 0; i < q; ++i){
            while(nowl < query[i].l){
                if(~nxt[nowl]){
                    update(nxt[nowl], 1);
                }
                ++nowl;
            }
            ans[query[i].id] = pre[query[i].r] - pre[query[i].l-1] + Query(query[i].l, query[i].r);
        }
        for(int i = 0; i < q; ++i){
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}

全部评论
胖队优秀
点赞 回复 分享
发布于 2018-07-20 16:17
if(~last[a[i]]){ nxt[last[a[i]]] = i; } 你好 请问第65行这里的代码是否可以直接写为 nxt[last[a[i]]] = i; 因为 last[] 数组的初值是0  不会取到-1
点赞 回复 分享
发布于 2018-07-20 20:39
请问树状数组求和表示的是1~l中出现的不同种类的数字的个数吗?
点赞 回复 分享
发布于 2018-07-20 20:01
弱弱的问一句,为什么相同的数字不会重复记到数组里 QAQ
点赞 回复 分享
发布于 2018-07-20 16:42

相关推荐

不愿透露姓名的神秘牛友
昨天 20:37
点赞 评论 收藏
分享
头像
04-27 15:11
已编辑
华东师范大学 算法工程师
暑期实习从2月开始投,面了两个月,流程该挂的都挂完了,腾讯字节一共号称是1.7w个hc,不知道都发给谁了,估计今年秋招要难顶。Timeline米哈游、美团、蚂蚁、微软等公司直接简历挂穿,没进面。携程:3.3&nbsp;投递、测评3.12&nbsp;笔试3.18&nbsp;一面3.25&nbsp;二面4.13&nbsp;ai面(hr面)4.14&nbsp;英语测评4.23&nbsp;offer(已拒)腾讯:2.6&nbsp;测评2.28&nbsp;wxg一面3.5&nbsp;wxg二面(挂)3.11&nbsp;teg一面3.21&nbsp;teg二面(取消)3.31&nbsp;teg一面4.10&nbsp;teg二面(挂)4.21&nbsp;wxg一面4.24&nbsp;wxg二面(挂)字节:1.28&nbsp;aml约面(取消)3.17&nbsp;火山一面(挂)4.8&nbsp;aml一面(挂)4.20&nbsp;抖音data一面(挂)阿里:3.23&nbsp;投递、测评3.28&nbsp;笔试3.31&nbsp;淘天一面4.8&nbsp;钉钉一面4.9&nbsp;淘天二面4.10&nbsp;阿里控股一面4.12&nbsp;钉钉二面(取消)4.15&nbsp;淘天hr面4.16&nbsp;淘天offer(已接)4.21&nbsp;高德一面(取消)4.22&nbsp;淘宝闪购一面(取消)面试最大的感触是,现在撞上ai转型,一堆老业务急着转向,新业务非常不成熟,研究型的组bar非常高根本进不去,业务侧挂着算法的岗位干的都是工程活,面试却又要问算法,另外agent的落地也远没有那么广,绝大多数还是那套写死的系统调一下llm&nbsp;api或者做做rag,其余少部分真的在搭agent的,基本不能在线上服务用什么很智能的模型,现阶段成本太高,进去大概率就是给垃圾模型从工程方面兜底,除了业务场景的应用和数据经验以外,技术方面很难有什么提升。算法岗做不了基模的还是去搜广推好,之前判断失误了完全没投,秋招不知道还进不进得去。
嵌入式的小白:不错啊,淘天也是挺好的,恭喜
我的求职进度条
点赞 评论 收藏
分享
牛奶配面包:第二个经典博弈题目吧
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务