题意: 求区间内不同元素的个数。
做法:主席树,莫队等等。
主席树:又叫可持续化线段树。说白了就是每一个前缀和都建一颗线段树。表示出以该点结尾的区间的区间不同数的个数。 虽然说建立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),此时需要用到树状数组来辅助维护,因为我们修改一个点的值得时候,该点所建的这颗树及其以后建的树的信息都变了。都需要维护。