LightOj 1188 树状数组

LightOj 1188
题目链接:
http://www.bnuoj.com/v3/problem_show.php?pid=13000
题意:
给一个数列(len <= 1e5),数列里的数(num <= 1e5)。
现有q次询问(<=1e5),询问某个区间的不同的数有多少个。
思路:
明显的树状数组统计,自己写的时候不太清楚那个树状数组到底应该怎么维护。有点想用扫描线类似的思想,然后离线所有询问这样去维护,甚至想到了按照右区间排序。但是到底树状数组应该怎样维护就不知道了。
题解接着往下。离线所有询问,按照右端点排序,然后更新树状数组至当前遍历到询问的右端点。这样一来,树状数组里存的值都是在当前询问的左边。那么现在要去重,记录对于每个节点上一次出现的地方last[i],当前节点i。那么对于[last[i],i]这个区间,如果加以询问时,就会多出i这个点对答案的贡献,而之前更新last[i]的时候已经对树状数组有所更新。也就是说,需要对last[i]进行相应的update来达到容斥的效果。
再具体一点说,i肯定是在当前询问中有所贡献的,为了避免ans = query(R)-query(L-1)小(没有更新i以后)或者大(L-1在last[i]之前,这样i对答案贡献为2而不是正确的1),所以对这个区间[last[i],i]进行操作,使得这个区间内没有i这个数“存在”。
然后见代码。
再补几句,因为当前询问的区间的右端点肯定是在R这里,而我们要排除掉询问区间中i在询问区间、last[i]也在询问区间中的点。所以只要留下最新遍历到的点就好,大概是这个意思。
源码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define MAXN (100000 + 5)
int a[MAXN];
int last[MAXN], head[MAXN];
int tree[MAXN * 4];
int n, q;
void update(int a, int val){for(;a <= n ; a += a & -a) tree[a] += val;}
int query(int a)
{
    int ans = 0;
    for(int i = a ; i ; i -= i & -i) ans += tree[i];
    return ans;
}
struct Q
{
    int L, R;
    int id;
}que[MAXN];
bool cmp(Q a, Q b){return a.R < b.R;}
int res[MAXN];
int main()
{
    int T;  scanf("%d", &T);
    for(int cas = 1 ; cas <= T ; cas++){
        memset(head, 0, sizeof(head));
        memset(last, 0, sizeof(last));
        scanf("%d%d", &n, &q);
        for(int i = 1 ; i <= n ; i++){
            scanf("%d", &a[i]);
            last[i] = head[a[i]];
            head[a[i]] = i;
        }
//        for(int i = 1 ; i <= n ; i++) printf("last[%d] = %d\n", i, last[i]);
        memset(tree, 0, sizeof(tree));
//        for(int i = 1 ; i <= n ; i++) printf("query(%d) = %d\n", i, query(i));
//        for(int i = 1 ; i <= n ; i++) printf("%d ", tree[i]);
        for(int i = 1 ; i <= q ; i++)   scanf("%d%d", &que[i].L, &que[i].R), que[i].id = i;
        sort(que + 1, que + q + 1, cmp);
        int rear = 1;
        for(int i = 1 ; i <= q ; i++){
            while(rear <= que[i].R){
                update(rear, 1);
                if(last[rear]) update(last[rear], -1);
                rear++;
            }
            res[que[i].id] = query(que[i].R) - query(que[i].L - 1);
//            printf("res[%d] = %d\n", que[i].id, res[que[i].id]);
        }
        printf("Case %d:\n", cas);
        for(int i = 1 ; i <= q ; i++)   printf("%d\n", res[i]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值