题面去内网找
这道题的思路很神奇,把集合分成了重集合和轻集合。我们把元素个数大于sqrt(n)的集合称为重集合。显然这样的集合超不过sqrt(n)个。
那么就可以分别处理集合了
首先统计出每个集合与每一个重集合交集有多大。维护重集合的sum(总和)和add(这个集合累计加了多少)值。
对于重集合
1.修改:只改一下add就好
2.查询:sum[x]+add所有重集合×与其交集
对于轻集合
1.修改:暴力。
2查询:暴力加+那一堆add
其实这道题的灵感来源于树链剖分的轻重之分
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 100005
#define ll long long
using namespace std;
struct road{
int v,next;}lu[N*4];
int n,m,q,h,e,tot,ss[N],adj1[N],adj2[N],vis[N];
ll a[N],cnt[N][320],sum[320],add[320];
void add1(int u,int v){lu[++e]=(road){v,adj1[u]};adj1[u]=e;}
void add2(int u,int v){lu[++e]=(road){v,adj2[u]};adj2[u]=e;}
int main()
{//freopen("20.in","r",stdin);//freopen(".out","w",stdout);scanf("%d%d%d",&n,&m,&q);ll x,y,z;h=sqrt(n);for(int i=1;i<=n;i++)scanf("%lld",&a[i]);for(int i=1;i<=m;i++){scanf("%lld",&y);if(y>=h)++tot,vis[i]=1,ss[i]=tot;for(int j=1;j<=y;j++){scanf("%lld",&x),add1(i,x);if(vis[i]==1)sum[ss[i]]+=a[x],add2(x,ss[i]);}}for(int i=1;i<=m;i++)for(int k=adj1[i];k;k=lu[k].next){int to=lu[k].v;for(int j=adj2[to];j;j=lu[j].next)cnt[i][lu[j].v]++;}ll ans=0;while(q--){scanf("%lld",&z);if(z==1){scanf("%lld",&x);ans=0;if(!vis[x]){for(int i=adj1[x];i;i=lu[i].next)ans+=a[lu[i].v];for(int i=1;i<=tot;i++)ans+=add[i]*cnt[x][i];}else{ans=sum[ss[x]];for(int i=1;i<=tot;i++)ans+=add[i]*cnt[x][i];}printf("%lld\n",ans);}else{scanf("%lld%lld",&x,&y);if(!vis[x]){for(int i=adj1[x];i;i=lu[i].next)a[lu[i].v]+=y;for(int i=1;i<=tot;i++)sum[i]+=y*cnt[x][i];}else add[ss[x]]+=y;}}
}