Galahad(板子:区间不重复数字的和,树状数组/线段树)

7 篇文章 0 订阅
5 篇文章 0 订阅

链接:https://ac.nowcoder.com/acm/contest/1084/B
来源:牛客网

Galahad
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
本题推荐BGM:Galahad and Scientific Witchery

魔女要测试骑士的能力,要求他维护一个长度为 的序列,每次要询问一个区间的和。

但是魔女觉得太简单了,骑士能轻松记住 个数作为前缀和。

于是,魔女要求他回答一个区间的和,但如果某一个数在这个区间出现了多次,这个数只能被计算一次。

输出描述:
输出 行,每行一个整数表示这次询问的答案。
示例1
输入
复制

5 3
1 3 3 2 1
1 5
1 3
2 4

输出
复制

6
4
5

/*
区间不同数字个数板子一样~
*/
解法一:
数状数组:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN = 5e5+5;
int a[MAXN];
LL b[MAXN];
LL ans[MAXN];
int n,m;
struct Node
{
    int l,r;
    int id;
    bool operator<(const Node &a)const
    {
        return r < a.r;
    }
} node[MAXN];
int last[MAXN];
inline int lowbit(int x)
{
    return x&-x;
}
void add(int x,int v)
{
    for(int i = x; i <= n; i+=lowbit(i))
    {
        b[i] += v;
    }
}
LL sum(int x)
{
    LL res = 0;
    for(int i = x; i > 0; i-=lowbit(i))
    {
        res += b[i];
    }
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++)
    {
       scanf("%d",&a[i]);
    }
    for(int i = 1; i <= m; i++)
    {
        scanf("%d%d",&node[i].l,&node[i].r);
        node[i].id = i;
    }
    /*
    按询问区间中r小的优先排序(r递增排序)
    这样维护的时候就可以只保留最后一个不重复的数字
    */
    sort(node+1,node+m+1);
    for(int i = 1; i <= m; i++)
    {
        for(int j = node[i-1].r+1; j <= node[i].r; j++)
        {
            if(last[a[j]])//前面出现过,那个位置置为0
                add(last[a[j]],-a[j]);
            add(j,a[j]);//当前位置加当前位置对应的值
            last[a[j]] = j;//记录位置
        }
        ans[node[i].id] = sum(node[i].r)-sum(node[i].l-1);
    }
    for(int i = 1; i <= m; i++)
    {
        printf("%lld\n",ans[i]);
    }
    return 0;
}

解法二:
线段树:

#include <bits/stdc++.h>
#define lson rt<<1,l, mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int N = 5e5+5;
typedef long long LL;
LL sum[N<<2];
int a[N];
int last[N];
LL ans[N];
struct Node
{
    int l,r;
    int id;
    bool operator<(const Node &a)const
    {
        return r < a.r;
    }
} node[N];
inline void push_up(int rt)
{

    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
/*
void build(int rt,int l,int r)
{
    if(l == r) //叶子节点
    {
        sum[l] = a[l];
        return;
    }
    int mid = (l+r)>>1;
    //建左子树
    build(lson);
    //建右子树
    build(rson);
    //向上更新信息
    push_up(rt);
}
*/
//单点更新
void update_point(int pos,int v,int rt,int l,int r)
{
    if(l == r)
    {
        sum[rt] = v;
        return;
    }
    int mid = (l+r) >> 1;
    if(pos <= mid) //在左子树
        update_point(pos,v,lson);
    else
        update_point(pos,v,rson);
    //向上更新信息
    push_up(rt);
}
//区间查询,查询【L,R】
LL query(int L,int R,int rt,int l,int r)
{
    if(L <= l && r <= R) //是查询区间子集
    {
        return sum[rt];
    }
    int mid = (l + r) >> 1;
    LL res = 0;
    if(L <= mid) res += query(L,R,lson);
    if(R > mid) res += query(L,R,rson);
    return res;

}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i = 1; i <= m; i++)
    {
        scanf("%d%d",&node[i].l,&node[i].r);
        node[i].id = i;
    }
    sort(node+1,node+m+1);
    //build(1,1,n);
    for(int i = 1; i <= m; i++)
    {
        for(int j = node[i-1].r+1; j <= node[i].r; j++)
        {
            if(last[a[j]])
                update_point(last[a[j]],0,1,1,n);
            last[a[j]] = j;
            update_point(j,a[j],1,1,n);

        }
        ans[node[i].id] = query(node[i].l,node[i].r,1,1,n);
    }

    for(int i = 1; i <= m; i++)
    {
        printf("%lld\n",ans[i]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leo Bliss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值