题意:给出n个数,m组序列,然后每次可以选择一个序列给对应的每个数加定值,及询问该组对应数之和
思路:可以知道修改某一组后,肯定对应和其他组有相交部分也要对应修改,每次去修改肯定不现实,然后对于sqrt(n)以内的数可以O(n)修改,其他的只要计算对应的增量即可,然后维护对于大于sqrt(n)的序列会影响到哪些小于sqrt(n)的序列即可。
代码:
#include <cstdio> #include <cmath> #include <vector> using namespace std; typedef long long ll; const int N = 100005; const int M = 333; int n , m , q; int size[N], id[N]; int bigcnt = 0, vis[N]; int inter[N][M]; ll a[N]; ll ans[N] , update[N]; vector<int> num[N]; int main() { scanf("%d%d%d" , &n , &m , &q); for (int i = 1 ; i <= n ; i ++) { scanf ("%I64d" , &a[i]); } int limit = (int)sqrt (n) + 1; for (int i = 1 ; i <= m ; i ++) { scanf ("%d" , &size[i]); num[i].resize(size[i]); for (int j = 0 ; j < size[i] ; j ++) { scanf ("%d" , &num[i][j]); } if (size[i] >= limit) { id[i] = bigcnt++; } else id[i] = -1; } for (int i = 1 ; i <= m ; i ++) if (id[i] != -1) { for (int j = 0 ; j < size[i] ; j ++) { vis[num[i][j]] = i; ans[id[i]] += a[num[i][j]]; } for (int j = 1 ; j <= m ; j ++) { for (int k = 0 ; k < size[j] ; k ++) if (vis[num[j][k]] == i) inter[j][id[i]]++; } } while (q --) { char str[5];int idx; scanf ("%s%d" , str , &idx); if (str[0] == ‘?‘) { if (id[idx] == -1) { ll ret = 0; for (int i = 0 ; i < size[idx] ; i ++) ret += a[num[idx][i]]; for (int i = 0 ; i < bigcnt ; i ++) ret += 1LL * update[i] * inter[idx][i]; printf ("%I64d\n" , ret); } else printf ("%I64d\n" , ans[id[idx]]); } else { int x; scanf ("%d" , &x); for (int i = 0 ; i < bigcnt ; i ++) ans[i] += 1LL * inter[idx][i] * x; if (id[idx] == -1) { for (int i = 0 ; i < size[idx] ; i ++) a[num[idx][i]] += x; } else update[id[idx]] += x; } } return 0; }
【分块】CODEFORCES 384C Subset Sums
原文:http://www.cnblogs.com/Rojo/p/4737596.html