HDU3333 线段树+离线询问

12 篇文章 0 订阅
9 篇文章 0 订阅

After inventing Turing Tree, 3xian always felt boring when solving problems about intervals, because Turing Tree could easily have the solution. As well, wily 3xian made lots of new problems about intervals. So, today, this sick thing happens again…

Now given a sequence of N numbers A1, A2, …, AN and a number of Queries(i, j) (1≤i≤j≤N). For each Query(i, j), you are to caculate the sum of distinct values in the subsequence Ai, Ai+1, …, Aj.
Input
The first line is an integer T (1 ≤ T ≤ 10), indecating the number of testcases below.
For each case, the input format will be like this:
* Line 1: N (1 ≤ N ≤ 30,000).
* Line 2: N integers A1, A2, …, AN (0 ≤ Ai ≤ 1,000,000,000).
* Line 3: Q (1 ≤ Q ≤ 100,000), the number of Queries.
* Next Q lines: each line contains 2 integers i, j representing a Query (1 ≤ i ≤ j ≤ N).
Output
For each Query, print the sum of distinct values of the specified subsequence in one line.
Sample Input
2
3
1 1 4
2
1 2
2 3
5
1 1 2 1 3
3
1 5
2 4
3 5
Sample Output
1
5
6
3
6
题意:求区间[l,r]中不同数字的和。例如 1 1 4 的和是1+4=5
思路:莫队很容易t,但是有一位大佬过了。离线询问,将所有区间按r从小到打排序,取一个指针pos,从第一个位置开始扫,用map来记录每个数字最后出现的位置。pos扫过来的时候,如果mp[a[pos]]为0,说明a[pos]这个数字是第一次出现,如果不为0,说明a[pos]之前出现过,并且出现的位置用mp[a[pos]]来记录,这个时候就把mp[a[pos]]这个单点更新为0,但不影响将pos的位置更新为a[pos]。(每一次出现相同数字的时候都是先把这个数字之前的记录去掉,再重新把这个数字加进来)pos每扫过一个数字,都要用map记录这个数字的最新位置。每当pos扫到询问的右端点时,可以用querry对这个区间进行查询,记录答案。

#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<string.h>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int N=30005;
struct node
{
    int l,r,id;
    bool operator <(const node &a)const 
    {
        return r<a.r;
    }
}ask[100005];
struct Tree{
    ll l, r, sum, add;
}tree[N<<2];
ll a[N];
map<ll,int>mp;
//延迟更新 
void pushup(int root){//儿子把信息传递给父亲
    if(tree[root].l==tree[root].r)return ;
    tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;   //<<1:*2; <<1|1:*2+1
    return ;
}
 void pushdown(int root){//父亲把自己的信息传给儿子
    if(tree[root].l==tree[root].r)return ;
    if(tree[root].add==-1)return ;
    tree[root<<1].add=tree[root<<1|1].add=tree[root].add;
    tree[root<<1].sum=(tree[root<<1].r-tree[root<<1].l+1)*tree[root].add; //长度*父亲.add
    tree[root<<1|1].sum=(tree[root<<1|1].r-tree[root<<1|1].l+1)*tree[root].add;
    tree[root].add=-1;
    return ;
}
//初始化 
void build(int l,int r,int root){
    tree[root].l=l;
    tree[root].r=r;
    tree[root].sum=0;
    tree[root].add=-1;
    if(l==r){//直到单个区间位置
        tree[root].sum=0;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,root<<1);//左区间
    build(mid+1,r,root<<1|1); //右区间
    pushup(root);//把儿子的信息更新到父亲
    return;
}
//更新区间 
void update(int l,int r,ll z,int root){
    if(l==tree[root].l&&tree[root].r==r){
        tree[root].sum=(tree[root].r-tree[root].l+1)*z;
        tree[root].add=z;//把要更新的内容保存下来,等到要用儿子时                                    再去更新
        return ;
    }
    pushdown(root);//用父亲的信息更新儿子
    int mid=tree[root].l+tree[root].r>>1;
    if(r<=mid)update(l,r,z,root<<1);
    else if(l>mid)update(l,r,z,root<<1|1);
    else {
        update(l,mid,z,root<<1);
        update(mid+1,r,z,root<<1|1);
    }
    pushup(root);//更新父亲
    return ;
}
//查找区间信息 
ll query(int l,int r,int root){
    if(l==tree[root].l&&tree[root].r==r){
        return tree[root].sum;
    }
    pushdown(root);
    int mid=tree[root].l+tree[root].r>>1;
    if(r<=mid)return query(l,r,root<<1);
    else if(l>mid)return query(l,r,root<<1|1);
    else return query(l,mid,root<<1)+query(mid+1,r,root<<1|1);
}
ll ans[100005];
int T,n,m;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(a,0,sizeof(a));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            mp[a[i]]=0;
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&ask[i].l,&ask[i].r);
            ask[i].id=i;
        }
        sort(ask+1,ask+1+m);
        int pos=1;//指针 
        build(1,n,1);
        for(int i=1;i<=m;i++)
        {
            while(pos<=ask[i].r)
            {
                int j=mp[a[pos]];
                if(j>0)//pos不是最后一次出现 
                {
                    update(j,j,0,1);
                } 
                update(pos,pos,a[pos],1);
                mp[a[pos]]=pos;
                pos++;
            }
            ans[ask[i].id]=query(ask[i].l,ask[i].r,1);
        }
        for(int i=1;i<=m;i++)
        printf("%lld\n",ans[i]);
    } 
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值