題意:統計一段序列【L,R】的和,重複元素隻算一次。
解法:容易看出線上做很難處理重複的情況,幹脆全部講查詢讀進來,然後将查詢根據右端點排個序,然後離散化資料以後就可以操作了。
每次讀入一個數,如果這個數之前出現過,那麼删除之前出現的那個數,改加上這個數,然後進行所有右端點小于等于此時下标的查詢即可。
關于正确性,引用sdj222555的話來說,"觀察一個區間,我們可以發現,如果出現重複的,盡量删除左邊的,保留右邊的,那麼右端點相同的區間都可以進行查詢。"
代碼:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <map>
#define lll __int64
using namespace std;
#define N 30007
lll c[N],ans[3*N+10004];
map<int,int> mp;
int a[N],pos[N],n,b[N],d[N];
struct Query
{
int L,R,ind;
}Q[3*N+10004];
int cmp(Query ka,Query kb)
{
return ka.R < kb.R;
}
inline int lowbit(int x) { return x&(-x); }
void modify(int pos,lll val)
{
while(pos <= n)
{
c[pos] += val;
pos += lowbit(pos);
}
}
lll getsum(int pos)
{
lll ans = 0;
while(pos > 0)
{
ans += c[pos];
pos -= lowbit(pos);
}
return ans;
}
int main()
{
int t,i,j,q;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&a[i]),b[i] = a[i];
scanf("%d",&q);
for(i=1;i<=q;i++)
scanf("%d%d",&Q[i].L,&Q[i].R),Q[i].ind = i;
memset(c,0,sizeof(c));
sort(b+1,b+n+1);
sort(Q+1,Q+q+1,cmp);
int ind = unique(b+1,b+n+1)-b-1;
for(i=1;i<=ind;i++)
mp[b[i]] = i;
for(i=1;i<=n;i++) //d[i]為a[i]離散化後的數
d[i] = mp[a[i]];
memset(pos,0,sizeof(pos)); //pos 存上次出現的位置
j = 1;
for(i=1;i<=n;i++)
{
if(pos[d[i]]) modify(pos[d[i]],-a[i]);
modify(i,a[i]);
pos[d[i]] = i;
while(j <= q && Q[j].R == i)
{
ans[Q[j].ind] = getsum(Q[j].R)-getsum(Q[j].L-1);
j++;
}
if(j > q)
break;
}
for(i=1;i<=q;i++)
printf("%I64d\n",ans[i]);
}
return 0;
}
View Code