SPOJ DQUERY

题意: 求区间内不同元素的个数。

做法:主席树,莫队等等。

主席树:又叫可持续化线段树。说白了就是每一个前缀和都建一颗线段树。表示出以该点结尾的区间的区间不同数的个数。  虽然说建立n棵线段树,其实每颗线段树只有longn个点,来维护整个树的信息。因为每两个相邻的线段树只有一个值不同,那么我们就刚好就是这long n个点。

不会主席树的可以看看代码。试着将这那个线段树画出来,看看他们每个点的信息,差不多也就理解了主席树了。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
#include<iostream>
#include<complex>
#include<string>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define nn 30100
#define mm 3000300
//#define ll long long
#define ULL unsiged long long
#define pb push_back
#define mod 1000000007
#define inf 0xfffffffffff
#define eps 0.00000001

struct chairtree
{
	int lson, rson, sum;
};
chairtree t[nn * 30];
int sz;
int root[nn];
void chair_init()
{
	sz = 1;
	t[0].lson = 0;
	t[0].rson = 0;
	t[0].sum = 0;
}

void insert(int num, int& x, int l, int r, int val)//加点
{
	t[sz++] = t[x]; x = sz - 1;
	t[x].sum += val;
	if (l == r) return;
	int mid = (l + r) >> 1;
	if (num <= mid) insert(num, t[x].lson, l, mid, val);
	else insert(num, t[x].rson, mid + 1, r, val);
}

int query(int x, int ll, int l, int r)//在x号子树上找到区间[1,ll]的和。
{
	if (l >= ll) return t[x].sum;
	int mid = (l + r) >> 1;
	int ans = 0;
	if (ll <= mid) ans += query(t[x].lson, ll, l, mid);
	ans += query(t[x].rson, ll, mid + 1, r);
	return ans;
}
int main()
{
	int n, q;
	while (scanf("%d", &n) != EOF)
	{
		map<int, int>mp;
		chair_init();
		for (int i = 1; i <= n; i++)
		{
			int x;
			scanf("%d", &x);
			if (mp.find(x) == mp.end())
			{
				root[i] = root[i - 1];
				insert(i, root[i], 1, n,1);
			}
			else
			{
				int tmp=root[i-1];
				insert(mp[x], tmp, 1, n, -1);
				root[i] = tmp;
				insert(i,root[i], 1, n, 1);
			}
			mp[x] = i;
		}
		scanf("%d", &q);
		while (q--)
		{
			int l, r;
			scanf("%d%d", &l, &r);
			printf("%d\n", query(root[r], l, 1, n));
		}
	}
	return 0;
}

加点的时候是需要一个节点才建一个节点,比较省内存,使得他的存储复杂度在nlongn,如果没修改,时间复杂度为o(nlong n),而修改的时间复杂度没o(nlongn*long n),此时需要用到树状数组来辅助维护,因为我们修改一个点的值得时候,该点所建的这颗树及其以后建的树的信息都变了。都需要维护。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值