HDU 5919
CCPC的I题。
给出一个序列,问区间[l, r]中所有不同元素出现的第一个位置(取最左)组成的序列中的中位数。
第i个询问区间依赖于第i-1个询问的答案,所以是强制在线的。
比较经典的主席树维护区间种类问题的变形。
相同元素只取最左侧位置,所以对序列a,从a[n]到a[1]建立主席树,插入新元素到主席树中时取消相同元素的贡献,只保留最左侧元素。
查询[l, r]的时候,查询第l个版本的主席树就好,复杂度O(nlogn)。
这里需要注意一下(其实是主席树基本用法需要注意的东西),当对某版本进行多次单点更新的时候,每次都需要新增一条路径,必须保证不对以往的版本造成影响。
#include
#include
#include
#include
using namespace std;
#define maxn (200010)
int root[maxn], tot;
struct seg {
int lson, rson, val;
} T[maxn * 40];
void update(int l, int r, int &now, int pre, int i, int val)
{
int tmp = now;
now = ++tot;
T[now] = tmp ? T[tmp] : T[pre];
T[now].val += val;
if(l == r) return ;
int m = (l + r) >> 1;
if(i <= m) update(l, m, T[now].lson, T[pre].lson, i, val);
else update(m + 1, r, T[now].rson, T[pre].rson, i, val);
}
int query(int l, int r, int now, int K)
{
if(l == r) return r;
int m = (l + r) >> 1;
if(T[T[now].lson].val >= K)
return query(l, m, T[now].lson, K);
else
return query(m + 1, r, T[now].rson, K - T[T[now].lson].val);
}
int getsum(int l, int r, int now, int L, int R)
{
if(L <= l && r <= R) return T[now].val;
int m = (l + r) >> 1, ret = 0;
if(L <= m) ret += getsum(l, m, T[now].lson, L, R);
if(R > m) ret += getsum(m + 1, r, T[now].rson, L, R);
return ret;
}
int a[maxn], p[maxn];
int main()
{
int t, kase = 0;
cin >> t;
while(t--)
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(int i = n; i >= 1; i--)
{
if(p[a[i]]) update(1, n, root[i], root[i+1], p[a[i]], -1);
update(1, n, root[i], root[i+1], i, 1);
p[a[i]] = i;
}
printf("Case #%d:", ++kase);
for(int i = 0, l, r, ans = 0, sum; i < m; i++)
{
scanf("%d%d", &l, &r);
l = (l + ans) % n + 1;
r = (r + ans) % n + 1;
if(l > r) swap(l, r);
sum = getsum(1, n, root[l], l, r);
sum = sum / 2 + sum % 2;
ans = query(1, n, root[l], sum);
printf(" %d", ans);
}
printf("\n");
while(tot)
{
T[tot].lson = T[tot].rson = 0;
T[tot--].val = 0;
}
for(int i = 1; i <= n; i++)
{
root[i] = 0;
p[a[i]] = 0;
}
tot = 0;
}
return 0;
}