HDU 3874 Necklace
线段树,离线询问
题意
给一个数组,m个询问。每次询问一个区间,求区间内数的和,重复数只计算一次。
思路
离线询问后按右端点排序,从左往右扫数字,开个hash记录上次该数出现的位置。线段树模拟数组,出现就在位置上放数,没出现就置零,统计区间和。
这样每次扫到一个数,如果他没出现,就放到线段树上;如果他出现了,那么把它从上次出现的位置删掉,放到新的位置,更新hash数组。
每次询问统计区间和。
代码
1A就是美滋滋
#include<bits/stdc++.h>
#define M(a,b) memset(a,b,sizeof(a))
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long LL;
using namespace std;
const int MAXN=50007;
const int oo=0x3f3f3f3f;
LL stree[MAXN<<2];
void pushup(int rt)
{
stree[rt]=stree[rt<<1]+stree[rt<<1|1];
}
void build(int l, int r, int rt)
{
M(stree, 0);
}
void update(int pos, int c, int l, int r, int rt)
{
if(l==r) { stree[rt]+=c;return; }
int mid=(l+r)>>1;
if(pos<=mid) update(pos, c, lson);
else update(pos, c, rson);
pushup(rt);
}
LL query(int L, int R, int l, int r, int rt)
{
if(L<=l&&r<=R) return stree[rt];
int mid=(l+r)>>1;
LL res=0;
if(L<=mid) res+=query(L, R, lson);
if(mid<R) res+=query(L, R, rson);
return res;
}
int num[MAXN];
int ha[1000007];
struct Query
{
int l, r;
LL ans;
int ne;
}q[MAXN<<2];
int head[MAXN];
int main()
{
int T;scanf("%d", &T);
while(T--)
{
int n;scanf("%d", &n);
for(int i=1;i<=n;i++)
scanf("%d", &num[i]);
int m;scanf("%d", &m);
build(1, n, 1);
M(head, -1), M(ha, 0);
for(int i=1;i<=m;i++)
{
scanf("%d%d", &q[i].l, &q[i].r);q[i].ans=0;
q[i].ne=head[q[i].r];head[q[i].r]=i;
}
for(int i=1;i<=n;i++)
{
if(ha[num[i]]==0)
{
update(i, num[i], 1, n, 1);
ha[num[i]]=i;
}
else
{
update(ha[num[i]], -num[i], 1, n, 1);
update(i, num[i], 1, n, 1);
ha[num[i]]=i;
}
for(int j=head[i];~j;j=q[j].ne)
{
int l=q[j].l, r=q[j].r;
q[j].ans=query(l, r, 1, n, 1);
}
}
for(int i=1;i<=m;i++)
{
printf("%lld\n", q[i].ans);
}
}
return 0;
}