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

这题……做法很多吧。据说莫队玄学多交几次就AC了。

题目要我们查询两个区间的不同的数的个数。我们先考虑一个区间内的做法。

这是一个经典的问题?(自行百度)

在线做法的话,只需要用可持久化线段树维护前个数中每个数最后一次出现的下标。

这样在扫一边的时候,如果某个数在前面出现过,就只要把前面的那个删掉,加上这个就好。

代码如下:

for (int i = 1; i <= n; i++)
{
    int v = a[i];
    if (~last[v])
    {
        int t = update(last[v], root[i - 1], -1, 1, n);
        root[i] = update(i, t, 1, 1, n);
    }
    else
        root[i] = update(i, root[i - 1], 1, 1, n);
    last[v] = i;
}

这样在查询的时候,我们只要在第个线段树中查询大于等于的数有多少个就行了。

现在考虑原来的问题。怎么处理两个区间?容易想到,把原来的序列在后面复制一次,那么原来的查询就等价于查询中不同的数的个数,那么这题就做完了。

(当然这个问题也是可以离线树状数组来做的)

完整代码(额,似乎比赛的时候判题机跑得比较快,如果过不了请自行开读入挂):

#include <bits/stdc++.h>
using namespace std;
const int N = 1 << 18;
int cnt = 0;
struct Node
{
    int l, r, sum;
} p[N << 5];
int update(int pos, int c, int v, int l, int r)
{
    int nc = ++cnt;
    p[nc] = p[c];
    p[nc].sum += v;
    if (l == r) return nc;
    int m = l + r >> 1;
    if (m >= pos)
        p[nc].l = update(pos, p[c].l, v, l, m);
    else
        p[nc].r = update(pos, p[c].r, v, m + 1, r);
    return nc;
}
int query(int pos, int c, int l, int r)
{
    if (l == r) return p[c].sum;
    int m = l + r >> 1;
    if (m >= pos)
        return p[p[c].r].sum + query(pos, p[c].l, l, m);
    return query(pos, p[c].r, m + 1, r);
}
int a[N];
int root[N];
int last[N];
int main()
{
    int n, q;
    while (~scanf("%d%d", &n, &q))
    {
        cnt = 0;
        memset(last, -1, sizeof last);
        for (int i = 1; i <= n; i++) scanf("%d", a + i);
        for (int i = 1; i <= n; i++) a[n + i] = a[i];
        int m = n;
        n <<= 1;
        for (int i = 1; i <= n; i++)
        {
            int v = a[i];
            if (~last[v])
            {
                int t = update(last[v], root[i - 1], -1, 1, n);
                root[i] = update(i, t, 1, 1, n);
            }
            else
                root[i] = update(i, root[i - 1], 1, 1, n);
            last[v] = i;
        }
        while (q--)
        {
            int x, y;
            scanf("%d %d", &x, &y);
            x += m;
            swap(x, y);
            printf("%d\n", query(x, root[y], 1, n));
        }
    }
    return 0;
}

不过非常可惜,判题机实在是太慢了。这个做法在比赛的时候也是卡过去的。
因此我们可以考虑离线操作,按查询的右端点递增的顺序来处理,这样求和就可以用树状数组来解决了。
代码如下:

#include <bits/stdc++.h>
using namespace std;
struct query
{
    int l, r, id;
};
int main()
{
    int n, q;
    while (~scanf("%d%d", &n, &q))
    {
        int m = n << 1 | 1;
        vector<int> a(m), bit(m);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for (int i = 1; i <= n; i++) a[n + i] = a[i];
        vector<query> v(q);
        vector<int> last(n + 1, -1), ans(q);
        for (int i = 0; i < q; i++) scanf("%d%d", &v[i].r, &v[i].l), v[i].id = i, v[i].r += n;
        sort(v.begin(), v.end(), [](const query& a, const query& b) { return a.r < b.r; });
        for (int i = 0, j = 1; i < q; i++)
        {
            for (; j <= v[i].r; j++)
            {
                if (~last[a[j]])
                    for (int k = last[a[j]]; k < m; k += k & -k) bit[k]--;
                for (int k = j; k < m; k += k & -k) bit[k]++;
                last[a[j]] = j;
            }
            int t = 0;
            for (int k = m; k; k -= k & -k) t += bit[k];
            for (int k = v[i].l - 1; k; k -= k & -k) t -= bit[k];
            ans[v[i].id] = t;
        }
        for(int i = 0; i < q; i++) printf("%d\n", ans[i]);
    }
    return 0;
}

全部评论
while(1) {     cout<<“蔡队牛逼”<<endl; }
点赞 回复 分享
发布于 2018-07-20 15:48
蔡队牛逼啊
点赞 回复 分享
发布于 2018-07-20 10:54
 毕竟上海全能王,蔡队nb
点赞 回复 分享
发布于 2018-07-19 20:57
蔡队牛逼
点赞 回复 分享
发布于 2018-07-19 20:53
蔡队牛逼
点赞 回复 分享
发布于 2018-07-19 20:41

相关推荐

积极的小学生不要香菜:你才沟通多少,没500不要说难
点赞 评论 收藏
分享
06-13 17:33
门头沟学院 Java
顺序不记了,大致顺序是这样的,有的相同知识点写分开了1.基本数据类型2.基本数据类型和包装类型的区别3.==和equals区别4.ArrayList与LinkedList区别5.hashmap底层原理,put操作时会发生什么6.说出几种树型数据结构7.B树和B+树区别8.jvm加载类机制9.线程池核心参数10.创建线程池的几种方式11.callable与runnable区别12.线程池怎么回收线程13.redis三剑客14.布隆过滤器原理,不要背八股,说说真正使用时遇到了问题没有(我说没有,不知道该怎么回答了)15.堆的内存结构16.自己在写项目时有没有遇见过oom,如何处理,不要背八股,根据真实经验,我说不会17.redis死锁怎么办,watchdog机制如何发现是否锁过期18.如何避免redis红锁19.一个表性别与年龄如何加索引20.自己的项目的QPS怎么测的,有没有真正遇到大数量表21.说一说泛型22.springboot自动装配原理23.springmvc与springboot区别24.aop使用过嘛?动态代理与静态代理区别25.spring循环依赖怎么解决26.你说用过es,es如何分片,怎么存的数据,1000万条数据怎么写入库中27.你说用limit,那么在数据量大之后,如何优化28.rabbitmq如何批次发送,批量读取,答了延迟队列和线程池,都不对29.计网知不知道smtp协议,不知道写了对不对,完全听懵了30.springcloud知道嘛?只是了解反问1.做什么的?短信服务,信息量能到千万级2.对我的建议,基础不错,但是不要只背八股,多去实际开发中理解。面试官人不错,虽然没露脸,但是中间会引导我回答问题,不会的也只是说对我要求没那么高。面完问我在济宁生活有没有困难,最快什么时候到,让人事给我聊薪资了。下午人事打电话,问我27届的会不会跑路,还在想办法如何使我不跑路,不想扣我薪资等。之后我再联系吧,还挺想去的😭,我真不跑路哥😢附一张河科大幽默大专图,科大就是大专罢了
查看30道真题和解析
点赞 评论 收藏
分享
哈哈哈哈哈哈哈哈哈哈这个世界太美好了
凉风落木楚山秋:毕业出路老师不管,你盖个章他好交差就完事了,等你盖完毕业了就不关他事情了
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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