CodeForces 348C Subset Sums(分块)(nsqrtn)

C. Subset Sums
time limit per test
3 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given an array a1, a2, ..., an and m sets S1, S2, ..., Sm of indices of elements of this array. Let's denote Sk = {Sk, i} (1 ≤ i ≤ |Sk|). In other words, Sk, i is some element from set Sk.

In this problem you have to answer q queries of the two types:

  1. Find the sum of elements with indices from set Sk: . The query format is "? k".
  2. Add number x to all elements at indices from set Sk: aSk, i is replaced by aSk, i + x for all i (1 ≤ i ≤ |Sk|). The query format is "+ k x".

After each first type query print the required sum.

Input

The first line contains integers n, m, q (1 ≤ n, m, q ≤ 105). The second line contains n integers a1, a2, ..., an (|ai| ≤ 108) — elements of array a.

Each of the following m lines describes one set of indices. The k-th line first contains a positive integer, representing the number of elements in set (|Sk|), then follow |Sk| distinct integers Sk, 1, Sk, 2, ..., Sk, |Sk| (1 ≤ Sk, i ≤ n) — elements of set Sk.

The next q lines contain queries. Each query looks like either "? k" or "+ k x" and sits on a single line. For all queries the following limits are held: 1 ≤ k ≤ m, |x| ≤ 108. The queries are given in order they need to be answered.

It is guaranteed that the sum of sizes of all sets Sk doesn't exceed 105.

Output

After each first type query print the required sum on a single line.

Please, do not write the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams or the %I64d specifier.

Examples
Input
5 3 5
5 -5 5 1 -4
2 1 2
4 2 1 4 5
2 2 5
? 2
+ 3 4
? 1
+ 2 1
? 2
Output
-3
4
9
【分析】一开始也想到了分块,但是一直以为是线段树,所以想歪了,然后看了网上的题解。。。太强了。

设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 <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <string>
#include <stack>
#include <queue>
#include <vector>
#define inf 0x3f3f3f3f
#define met(a,b) memset(a,b,sizeof a)
#define pb push_back
typedef long long ll;
using namespace std;
const int N = 1e5+10;
const int M = 24005;
int n,m,q,k;
int cnt[350][N],id[N],tot=0;
int is[N];
ll a[N],add[N],sum[N];
vector<int>g[N],bl[N],big;
int main() {
    int u,v;
    scanf("%d%d%d",&n,&m,&q);
    int b=sqrt(n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    for(int i=1;i<=m;i++){
        scanf("%d",&k);
        if(k>b)id[i]=++tot,big.pb(id[i]),is[i]=1;;
        for(int j=0;j<k;j++){
            scanf("%d",&u);

            g[i].pb(u);
            if(k>b)bl[u].pb(tot),sum[tot]+=a[u];;
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=0;j<g[i].size();j++){
            int u=g[i][j];
            for(int p=0;p<bl[u].size();p++){
                cnt[bl[u][p]][i]++;
            }
        }
    }
    char str[5];
    while(q--){
        scanf("%s",str);
        if(str[0]=='+'){
            scanf("%d%d",&u,&v);
            if(is[u]){
                add[id[u]]+=v;
            }
            else {
                for(int i=0;i<g[u].size();i++){
                    int c=g[u][i];
                    a[c]+=v;
                }
                for(int i=0;i<big.size();i++){
                    int c=big[i];
                    sum[c]+=v*cnt[c][u];
                }
            }
        }
        else {
            scanf("%d",&u);
            if(is[u]){
                ll ans=sum[id[u]];
                for(int i=0;i<big.size();i++){
                    int c=big[i];
                    ans+=add[c]*cnt[c][u];
                }
                printf("%lld\n",ans);
            }
            else {
                ll ans=0;
                for(int i=0;i<g[u].size();i++){
                    ans+=a[g[u][i]];
                }
                for(int i=0;i<big.size();i++){
                    int c=big[i];
                    ans+=add[c]*cnt[c][u];
                }
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

 


posted @ 2017-03-04 21:53  贱人方  阅读(331)  评论(0编辑  收藏  举报