显然是一个比较简单的离线查询问题。
本质上是对区间求集合的问题,按照区间右端点从小到大对区间进行排序,然后用一个指针curr表示当前正在考虑区间的最右侧位置,针对排好序区间[tarl,tarr],先查看curr是否到达tarr,如果没到的话,就依次增加curr,并在增加的过程中,不断维护每种花出现的最晚位置,并把这朵花的魅力值更新进树状数组的相应位置(出现的最晚位置)处。然后就非常的简单了,要求的区间[tarl,tarr]的值就是sum(tarr) - sum(tarl-1)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
struct P {
int first;
int second;
int id;
friend bool operator<(P p1,P p2){
if(p1.second == p2.second)
return p1.first < p2.first;
return p1.second < p2.second;
}
};
const int MAX = 100008;
P ps[MAX];
int a[MAX];
vector<int> shop[MAX];
int n,m,q;
int maps[MAX];
int ans[MAX];
int bitree[MAX];
inline int lowbit(int x){
return x&(-x);
}
int add(int pos,int val){
while(pos <= n){
bitree[pos] += val;
pos += lowbit(pos);
}
}
int sum(int pos){
int ans = 0;
while(pos > 0){
ans += bitree[pos];
pos -= lowbit(pos);
}
return ans;
}
int main(){
int T;scanf("%d",&T);
while(T--){
for(int i = 0;i < MAX;i++) shop[i].clear();
memset(maps,0,sizeof(maps));
memset(ans,0,sizeof(ans));
memset(bitree,0,sizeof(bitree));
scanf("%d",&m);
for(int i = 1;i <= m;i++){
scanf("%d",&a[i]);
}
scanf("%d",&n);
for(int i = 1;i <= n;i++){
int c;scanf("%d",&c);
for(int j = 1;j <= c;j++){
int v;scanf("%d",&v);
shop[i].push_back(v);
}
}
scanf("%d",&q);
for(int i = 1 ;i <= q;i++){
int l,r;
scanf("%d%d",&l,&r);
ps[i].first = l;
ps[i].second = r;
ps[i].id = i;
}
sort(ps+1,ps+q+1);
int curr = 0;
for(int i = 1;i <= q;i++){
int tarl = ps[i].first;
int tarr = ps[i].second;
int id = ps[i].id;
if(tarr != curr){
while(curr != tarr){
curr++;
for(int t = 0;t < shop[curr].size();t++){
int item = shop[curr][t];
if(maps[item]){
add(maps[item],-a[item]);
}
maps[item] = curr;
add(maps[item],a[item]);
}
}
}
ans[id] = sum(tarr) - sum(tarl-1);
}
for(int i = 1;i <= q;i++){
printf("%d\n",ans[i]);
}
}
return 0;
}