hdu 3333 Turing Tree 线段树

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3333

题意:给出n个数的一个序列,询问m次,每次询问区间[i,j]内改序列不同数之和,就是说该区间如果某个数出现多次,则只算一次。


这个题要从正面去做太难太难,反正我是不会。

线段树域保存区间不同数之和。

先把所有的询问保存下来,然后以右端点递增排序,遇到右端点相同的怎么排都无所谓。

然后从左到右跟新序列的值,更新到第i个值时检查之前有没有出现过与个这个值相等的,有的话就把之前那个节点赋0,再插入第i个数,如果查询区间右端点等于i,那么查询这个区间的答案并保存。

保存已更新数下标我用了个map,会比较慢,也可以离散来弄、、、


#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
using namespace std;
#define ll __int64
#define MAXN 300050//30005会WA,不晓得为什么、、、
ll num[MAXN];
struct node
{
	int l,r;
	ll s;
}t[MAXN*4];
struct ele
{
	int l,r;
	ll ans;
	int dix;
}a[MAXN];
bool cmp1(ele a,ele b)
{
	return a.r<b.r;
}
bool cmp2(ele a,ele b)
{
	return a.dix<b.dix;
}
void construct(int l,int r,int p)
{
	t[p].l=l,t[p].r=r,t[p].s=0;
	if(l==r) return ;
	int m=(l+r)>>1;
	construct(l,m,p<<1);
	construct(m+1,r,p<<1|1);
}
void insert(int x,ll val,int p)
{
	if(t[p].l==t[p].r)
	{
		t[p].s=val;
		return ;
	}
	int m=(t[p].l+t[p].r)>>1;
	if(x<=m) insert(x,val,p<<1);
	else insert(x,val,p<<1|1);
	t[p].s=t[p<<1].s+t[p<<1|1].s;
}
ll query(int l,int r,int p)
{
	if(t[p].l==l&&t[p].r==r)
		return t[p].s;
	int m=(t[p].l+t[p].r)>>1;
	if(r<=m) return query(l,r,p<<1);
	else if(l>m) return query(l,r,p<<1|1);
	else return query(l,m,p<<1)+query(m+1,r,p<<1|1);
}
int main()
{
	int n,m,cas,i,j;
	scanf("%d",&cas);
	map<ll ,int> vis;
	while(cas--)
	{
		vis.clear();
		scanf("%d",&n);
		construct(1,n,1);
		for(i=1;i<=n;i++) scanf("%I64d",&num[i]),vis[num[i]]=0;
		scanf("%d",&m);
		for(i=1;i<=m;i++) scanf("%d%d",&a[i].l,&a[i].r),a[i].ans=0,a[i].dix=i;
		sort(a+1,a+1+m,cmp1);
		for(i=1,j=1;i<=n;i++)
		{
			if(vis[num[i]])
				insert(vis[num[i]],0,1);
			insert(i,num[i],1);
			vis[num[i]]=i;
			while(a[j].r==i)
			{
				a[j].ans=query(a[j].l,a[j].r,1);
				j++;
				if(j>m) break;
			}
			if(j>m) break;
		}
		sort(a+1,a+1+m,cmp2);
		for(i=1;i<=m;i++)
			printf("%I64d\n",a[i].ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值