HDU-3333 Turing Tree(离线+树状数组/主席树)

题意

给定一个长度为 n n n 的序列, m m m 个查询,每次查询区间 [ L , R ] [L,R] [L,R] 范围内不同元素的和。
1 ≤ T ≤ 10 1\leq T \leq 10 1T10
1 ≤ n ≤ 30000 1 \leq n\leq 30000 1n30000
1 ≤ m ≤ 100000 1\leq m\leq 100000 1m100000

思路

这道题没有强制在线,又没有修改,离线会比在线好想。
可以从第 1 1 1 个数到第 n n n 个数一次添加,并去除之前的相同元素,以此为顺序。就是说对于 m m m 个询问,按右端点进行排序,以此添加进每个数字并只保留最右端的数,借助 m a p map map 去重,区间和用树状数组维护。
假如要强制在线,该怎么办呢?
假如我们能力开下 n n n 个树状数组,就可以在线的查询了,可是 n n n 个树状数组肯定开不下,那动态开点线段树?可以,但是每次也是要把原来的线段树复制一遍,复杂度一累,时间过不去。
主席树的作用就体现出来了,回顾离线的写法,每次只会修改 1 − 2 1-2 12 个位置,那在前缀的基础上,保留原来的历史版本不就行了?这就是可持久化,详见代码。

代码

#include
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=3e5+5;
const int NN=1e7+2e6+5;

struct ChairmanTree
{
	int lson[NN],rson[NN];LL sum[NN];
	int rt[N],tot;
	int &operator [](const int x){return rt[x];}
	void build()
	{
		memset(rt,0,sizeof(rt));
		sum[tot=0]=lson[0]=rson[0]=0;
	}
	void create(int &k){sum[++tot]=sum[k],lson[tot]=lson[k],rson[tot]=rson[k],k=tot;}
	void update(int &k,int x,int val,int l,int r)
	{
		create(k);
		if(l==r)
		{
			sum[k]+=val;
			return;
		}
		int mid=(l+r)>>1;
		if(x<=mid)update(lson[k],x,val,l,mid);
		else update(rson[k],x,val,mid+1,r);
		sum[k]=sum[lson[k]]+sum[rson[k]];
	}
	LL query(int k,int L,int R,int l,int r)
	{
		if(!k)return 0;
		if(L<=l&&r<=R)return sum[k];
		int mid=(l+r)>>1;
		if(R<=mid)return query(lson[k],L,R,l,mid);
		else if(L>mid)return query(rson[k],L,R,mid+1,r);
		else return query(lson[k],L,R,l,mid)+query(rson[k],L,R,mid+1,r);
	}
}CT;
map<int,int>mp;

int main()
{
	int T,n,m;
	scanf("%d",&T);
	while(T--)
	{
		mp.clear();
		scanf("%d",&n);
		CT.build();
		FOR(i,1,n)
		{
			int x;
			scanf("%d",&x);
			CT[i]=CT[i-1];
			if(mp[x])CT.update(CT[i],mp[x],-x,1,n);
			CT.update(CT[i],i,x,1,n);
			mp[x]=i;
		}
		scanf("%d",&m);
		while(m--)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			printf("%lld\n",CT.query(CT[y],x,y,1,n));
		}
	}
	return 0;
}

你可能感兴趣的:(题目)