Codeforces 348C - Subset Sums(根号分治)
对于这类不好直接维护的数据结构,第一眼应该想到……
根号分治!
我们考虑记【大集合】为大小 \(\geq\sqrt{n}\) 的集合,【小集合】为大小 \(<\sqrt{n}\) 的集合。
显然,查询/修改小集合的时候,直接暴力跑一遍不会出问题,时间复杂度 \(\mathcal O(n\sqrt{n})\)。
关键在于怎样处理【大集合】:
- 修改大集合的时候,暴力一个一个元素修改显然不行,于是考虑整体打一个 \(+v\) 的标记 \(tag_x\)
- 查询大集合的时候我们也不能遍历一遍集合求和,于是可以维护一个数组 \(ans_x\) 表示大集合 \(x\) 的答案,查询的时候直接输出 \(ans_x\)。
这样又有一个问题了,怎样将大集合的贡献累加入答案中。
注意到大集合有一个性质,那就是大集合的个数不会超过 \(\sqrt{n}\)。
故每一次集合整体加值的时候,暴力修改每个大集合的 \(ans_x\);询问小集合的时候,将每个大集合的 \(tag_x\) 累加进答案中。
至于怎样计算大集合的贡献,可以维护一个 \(same_{i,j}\) 表示第 \(i\) 个大集合与第 \(j\) 个集合有多少个公共的元素。那么你对某个大集合 \(x\) 做一次 \(+v\) 的操作,它对另一个集合 \(y\) 的贡献应为 \(same_{x,y}\times v\)。
总结下来,就是:
- 修改小集合 \(x\),暴力修改 \(a_i\),并且对于所有大集合 \(y\),令 \(ans_y\) 假设 \(same_{y,x}\times v\)。
- 修改大集合 \(x\),令 \(tag_x\) 加上 \(v\),并且对于所有大集合 \(y\),令 \(ans_y\) 假设 \(same_{y,x}\times v\)。
- 查询小集合 \(x\),暴力遍历一遍集合求出 \(a_i\) 的和,并枚举所有大集合 \(y\),令答案加上 \(same_{y,x}\times tag_y\)。
- 查询大集合 \(x\),直接输出 \(ans_x\) 就行了。
时空复杂度均为 \(n\sqrt{n}\)
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=1e5;
const int SQRT=316;
int n,m,qu;ll a[MAXN+5];
vector<int> s[MAXN+5];
int bg[SQRT+5],bgnum=0,same[MAXN+5][SQRT+5];
bitset<MAXN+5> vis[SQRT+5];
ll anss[MAXN+5],add[MAXN+5];
int main(){
scanf("%d%d%d",&n,&m,&qu);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=m;i++){
int len;scanf("%d",&len);
for(int j=1;j<=len;j++){
int x;scanf("%d",&x);s[i].pb(x);
anss[i]+=a[x];
}
if(len>=SQRT){
bg[++bgnum]=i;
for(int j=0;j<s[i].size();j++){
vis[bgnum][s[i][j]]=1;
}
}
}
for(int i=1;i<=m;i++)
for(int j=0;j<s[i].size();j++)
for(int k=1;k<=bgnum;k++)
if(vis[k][s[i][j]]) same[i][k]++;
while(qu--){
static char opt[3];scanf("%s",opt+1);
if(opt[1]=='?'){
int x;scanf("%d",&x);
ll ans=0;
if(s[x].size()<SQRT){
for(int j=0;j<s[x].size();j++) ans+=a[s[x][j]];
for(int j=1;j<=bgnum;j++) ans+=1ll*add[bg[j]]*same[x][j];
printf("%lld\n",ans);
} else printf("%lld\n",anss[x]);
} else {
int x,y;scanf("%d%d",&x,&y);
if(s[x].size()<SQRT){
for(int j=0;j<s[x].size();j++) a[s[x][j]]+=y;
for(int j=1;j<=bgnum;j++) anss[bg[j]]+=1ll*y*same[x][j];
} else {
for(int j=1;j<=bgnum;j++) anss[bg[j]]+=1ll*y*same[x][j];
add[x]+=y;
}
}
}
return 0;
}

浙公网安备 33010602011771号