显然是一个比较简单的离线查询问题。
本质上是对区间求集合的问题,按照区间右端点从小到大对区间进行排序,然后用一个指针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;
}