Turing Tree HDU - 3333
Turing Tree HDU - 3333
题意:
给你一个长度为n的数组, q次询问, 每次询问问区间\([l, r]\)不同元素之和是多少?
题解:
A: 这题用主席树咋写啊?
B:嗯嗯,这题其实用的是主席的思想而节约空间。如果这题不考虑空间问题, 我们可以建n颗普通线段树,且我们要保证第 \(i\) 颗线段树 中 a[i]只出现再第 \(i\) 个位置,且只出现一次(如果前面出现了就删点)。线段树维护区间和。
A: 如果考虑空间问题, 就可以想主席树一样,没个版本连接上一个版本。
B: 是的。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 30007;
typedef long long ll;
struct hjt{
int l, r;
ll sum;
}tree[40 * N];
int n, q, rt[N], top = 1;
ll a[N];
#define m (l + r) / 2
void update(ll v, int pos,int last, int &now, int l, int r){
now = top++;
tree[now] = tree[last];
if(l == r){
tree[now].sum = v;
return;
}
if(pos <= m) update(v, pos, tree[last].l, tree[now].l, l, m);
else update(v, pos, tree[last].r, tree[now].r, m + 1, r);
tree[now].sum = tree[tree[now].l].sum + tree[tree[now].r].sum;
}
ll query(int now, int ql, int qr, int l, int r){
if(ql <= l && qr >= r) return tree[now].sum;
ll ans = 0;
if(ql <= m) ans += query(tree[now].l, ql, qr, l, m);
if(qr > m) ans += query(tree[now].r, ql, qr, m + 1, r);
return ans;
}
map<ll, int>vis;
int main(){
int t; scanf("%d", &t);
while(t--){
memset(rt, 0, sizeof(rt));
vis.clear();
top = 1;
memset(tree, 0, sizeof(tree)); scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%lld", &a[i]);
}
for(int i = 1; i <= n; i++){
if(vis[a[i]] == 0){
update(a[i], i, rt[i - 1], rt[i], 1, n);
vis[a[i]] = i;
}else{
int temp;
update(0, vis[a[i]], rt[i - 1], temp, 1, n);
update(a[i], i, temp, rt[i], 1, n);
vis[a[i]] = i;
}
}
scanf("%d", &q);
while(q--){
int l, r;
scanf("%d %d", &l, &r);
ll ans = query(rt[r], l, r, 1, n);
printf("%lld\n", ans);
}
}
}