题意:
给n个数和m个集合, 一个集合就是n个数的下标。
有q个操作, 每个操作就是给一个集合里面代表的数加上v, 或者询问一个集合里面的数的和。
解法:
ORZ题解。。。
设B = sqrtn
先给集合分成两种, 一种是大小大于B的, 称为重集合, 小于的称为轻集合。
显然重集合的个数不会超过B个。
对每个集合, 求出它和每个重集合交集的大小, 即cnt[i][j]表示第i个集合和第j 个重集合交集的大小。 cnt数组可以O(NB)求出
对每个重集合, 维护add和sum, add表示加在这个集合上的值, sum表示这个集合没有加上其他重集合的附加值的和。
然后把操作分为4种:
1. 在轻集合上加值。 这样对每个轻集合里面的元素暴力加上v就行(O(B))。 同时要维护重集合的sum, 于是, 每个重集合的sum加上v*cnt[i][j],(O(B))。复杂度(O(B))。
2. 在重集合上加值。 直接在重集合的add上加上v。 (O(1))
3. 询问轻集合。 ans 加上轻集合里面的元素的值, (O(B))。 然后再加上重集合的add[j] * cnt[i][j], (O(B))。 O(B)
4. 询问重集合。 ans 加上重集合的sum, 然后再加上各个重集合的add[j] * cnt[i][j]。 O(B)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <vector> using namespace std; #define mnx 110020 #define B 350 #define LL long long #define PB push_back #define vi vector<int> LL add[B], sum[B]; int id[mnx]; //第i个集合的重集合标号 int cnt[mnx][B], c[mnx]; LL tot[mnx]; bool h[mnx]; // 第i个集合是不是重集合 vi g[mnx]; //集合 vi t[mnx]; // 第i个数被哪些重集合包括 int n, m, q, k; int main() { while(scanf("%d%d%d", &n, &m, &q) != EOF) { for(int i = 0; i < n; ++i) scanf("%I64d", &tot[i]); k = 0; for(int i = 0; i < m; ++i) { scanf("%d", &c[i]); if(c[i] >= B) { h[i] = 1; id[i] = k++; } else h[i] = 0; for(int j = 0; j < c[i]; ++j) { int u; scanf("%d", &u); --u; g[i].PB(u); if(h[i]) { // 重集合的话在包括u的重集合中加上k-1 t[u].PB(k - 1); sum[k-1] += tot[u]; } } } // memset(cnt, 0, sizeof cnt); for(int i = 0; i < m; ++i) { for(int j = 0; j < g[i].size(); ++j) { int u = g[i][j]; //第i个集合包含的下标 for(int kk = 0; kk < t[u].size(); ++kk) { int v = t[u][kk]; //重集合 cnt[i][v]++; } } } while(q--) { char op[4]; int p; LL x; scanf("%s%d", op, &p); --p; if(op[0] == '?') { LL ans = 0; if(h[p]) { ans += sum[id[p]]; for(int i = 0; i < k; ++i) ans += cnt[p][i] * add[i]; } else { for(int i = 0; i < g[p].size(); ++i) { ans += tot[g[p][i]]; } for(int i = 0; i < k; ++i) ans += cnt[p][i] * add[i]; } printf("%I64d\n", ans); } else { scanf("%I64d", &x); if(h[p]) { add[id[p]] += x; } else { for(int i = 0; i < g[p].size(); ++i) tot[g[p][i]] += x; for(int i = 0; i < k; ++i) sum[i] += cnt[p][i] * x; } } } } return 0; }