HDU 3333 Turing Tree (线段树+离散化+离线处理)

题目链接:
https://vjudge.z180.cn/problem/HDU-3333
题目大意:给你长度为N的序列,再给你Q次询问,每次询问给你一个区间 [ l , r ] , 按 询 问 顺 序 依 次 输 出 序 列 中 区 间 [ l , r ] 的 数 的 总 和 ( 重 复 值 只 记 录 一 次 ) [l,r],按询问顺序依次输出序列中区间[l,r]的数的总和(重复值只记录一次) [l,r][l,r]()(这Q次询问是一次性给出来的,这也是这道题可以离线处理的原因).

分析:这道题如果不要求重复值只记录一次,那么就直接裸的线段树求区间和的问题,但是加了一个条件之后难度就上升了,我们想如果一个数字x在 [ l , r ] [l,r] [l,r]这个区间多次出现,我们只把它最后一次出现的位置赋值为x即可,之前出现的位置都赋值为0,就可以满足要求了,所以我们用线段树去维护下标对应的值即可(单点修改),求区间总和就是直接(区间查询),但是这道题给你Q个区间,肯定要先处理区间靠前的也就是r小的,如果不按这样的顺序处理,那么就会乱套,例如:x在询问区间[1,3]出现了一次,而在询问区间[4,5]也出现了一次,那么如果我们先计算[4,5]这个区间的和,那么x最后出现的位置是不是就在区间[4,5],这时候再去计算区间[1,3],x最后出现的下标就不在区间[1,3]内了,也就是x这个值没有被计算进[1,3]的总和,所以答案出错.

AC代码:

#include<bits/stdc++.h>
using namespace std;
const int Maxn = 3e4+10;
const int Q = 1e5+10; 
typedef long long ll;
ll sum[Maxn<<2]; 
/*单点跟新*/
void pushup(int dep)
{
    
    
	sum[dep] = sum[dep<<1]+sum[dep<<1|1];
	return ;
}
void update(int pos,int val,int dep,int l,int r)
{
    
    
	if(l==r)
    {
    
    
    	sum[dep] = val;//叶子节点的这一步就想当于pushup(dep) 
		return ; 
	}
	int mid = l+r>>1;
	if(pos<=mid) update(pos,val,dep<<1,l,mid);
	else update(pos,val,dep<<1|1,mid+1,r);
	pushup(dep);//跟新完儿子节点之后,再回溯上来跟新父节点的值 
	return ;
}
ll query(int ql,int qr,int dep,int l,int r)
{
    
    
	if(ql<=l&&r<=qr)
	{
    
    
		return sum[dep];
	}
	ll ans = 0;
	int mid = l+r>>1;
	if(ql<=mid) ans+=query(ql,qr,dep<<1,l,mid);
	if(qr>mid) ans+=query(ql,qr,dep<<1|1,mid+1,r);
	return ans;
} 
struct Node
{
    
    
	int l,r,pos;
}node[Q];
int cmp(struct Node a,struct Node b)
{
    
    
	return a.r<b.r;
}
int vis[Maxn];
int a[Maxn];
int tmp[Maxn];
int pre[Maxn];//记录A[i]这个数上一次出现的位置 
ll ans[Q];
int main()
{
    
    
	int t;
	cin>>t;
	while(t--)
	{
    
    
		memset(vis,0,sizeof(vis));
		memset(a,0,sizeof(a));
		memset(tmp,0,sizeof(tmp));
		memset(pre,0,sizeof(pre));
		memset(ans,0,sizeof(ans));	
		int n;
		cin>>n;
		for(int i=1;i<=n;i++) 
		{
    
    
			cin>>a[i];
			tmp[i] = a[i];
		}
		sort(tmp+1,tmp+n+1);
		int m;//m次询问 
		cin>>m;
        for(int i=1;i<=m;i++)
        {
    
    
        	cin>>node[i].l>>node[i].r;
        	node[i].pos = i;
		}
		sort(node+1,node+m+1,cmp);
		for(int i=1,j=1;i<=n;i++)
		{
    
    
			int pos = lower_bound(tmp+1,tmp+n+1,a[i])-tmp;
//			cout<<pos<<"yy"<<'\n';
			if(!vis[pos])//没有出现过 
			{
    
    
				update(i,a[i],1,1,n);
				vis[pos] = 1;
				pre[pos] = i;
			}
			else
			{
    
    
				/*跟新位置*/
				update(pre[pos],0,1,1,n);//以前的位置已不再是最新,直接置0
				update(i,a[i],1,1,n);
				pre[pos] = i; 
			}
			for(;j<=m;j++)
			{
    
    
				/*i必须跟新到node[j].r了才能得到有[node[j].l,node[j].r]的和准确值*/
				if(i==node[j].r) ans[node[j].pos] = query(node[j].l,node[j].r,1,1,n);
				else break;
			}
		}
		for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
	}
}

猜你喜欢

转载自blog.csdn.net/TheWayForDream/article/details/116717730