题面传送门

对于这类不好直接维护的数据结构,第一眼应该想到……

根号分治!

我们考虑记【大集合】为大小 \(\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\)。

总结下来,就是:

  1. 修改小集合 \(x\),暴力修改 \(a_i\),并且对于所有大集合 \(y\),令 \(ans_y\) 假设 \(same_{y,x}\times v\)。
  2. 修改大集合 \(x\),令 \(tag_x\) 加上 \(v\),并且对于所有大集合 \(y\),令 \(ans_y\) 假设 \(same_{y,x}\times v\)。
  3. 查询小集合 \(x\),暴力遍历一遍集合求出 \(a_i\) 的和,并枚举所有大集合 \(y\),令答案加上 \(same_{y,x}\times tag_y\)。
  4. 查询大集合 \(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;
}

Codeforces 348C - Subset Sums(根号分治)的更多相关文章

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

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

  2. Codeforces 348C Subset Sums 分块思想

    题意思路:https://www.cnblogs.com/jianrenfang/p/6502858.html 第一次见这种思路,对于集合大小分为两种类型,一种是重集合,一种是轻集合,对于重集合,我们 ...

  3. Codeforces 1039D You Are Given a Tree [根号分治,整体二分,贪心]

    洛谷 Codeforces 根号分治真是妙啊. 思路 考虑对于单独的一个\(k\)如何计算答案. 与"赛道修建"非常相似,但那题要求边,这题要求点,所以更加简单. 在每一个点贪心地 ...

  4. [codeforces 509]C. Sums of Digits

    [codeforces 509]C. Sums of Digits 试题描述 Vasya had a strictly increasing sequence of positive integers ...

  5. 洛谷P1466 集合 Subset Sums

    P1466 集合 Subset Sums 162通过 308提交 题目提供者该用户不存在 标签USACO 难度普及/提高- 提交  讨论  题解 最新讨论 暂时没有讨论 题目描述 对于从1到N (1 ...

  6. Project Euler 106:Special subset sums: meta-testing 特殊的子集和:元检验

    Special subset sums: meta-testing Let S(A) represent the sum of elements in set A of size n. We shal ...

  7. Project Euler P105:Special subset sums: testing 特殊的子集和 检验

    Special subset sums: testing Let S(A) represent the sum of elements in set A of size n. We shall cal ...

  8. Project Euler 103:Special subset sums: optimum 特殊的子集和:最优解

    Special subset sums: optimum Let S(A) represent the sum of elements in set A of size n. We shall cal ...

  9. Codeforces348C - Subset Sums

    Portal Description 给出长度为\(n(n\leq10^5)\)的序列\(\{a_n\}\)以及\(m(m\leq10^5)\)个下标集合\(\{S_m\}(\sum|S_i|\leq ...

随机推荐

  1. 2020BUAA软工个人博客作业-软件案例分析

    2020BUAA软工个人博客作业-软件案例分析 17373010 杜博玮 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人博客作业-软件案例分 ...

  2. Noip模拟74 2021.10.11

    T1 自然数 考场上当我发现我的做法可能要打线段树的时候,以为自己百分之百是考虑麻烦了 但还是打了,还过掉了所有的样例,于是十分自信的就交了 正解还真是线段树,真就第一题数据结构 但是包括自己造的小样 ...

  3. Python课程笔记(十一)

    一.线程与多线程 1.线程与进程 线程指的是 进程(运行中的程序)中单一顺序的执行流. 多个独立执行的线程相加 = 一个进程 多线程程序是指一个程序中包含有多个执行流,多线程是实现并发机制的一种有效手 ...

  4. C++STL(set……)

    set 底层实现是用红黑树. set 建立 set<int> s; // 不可重,默认升序 set<int,less> s; // 不可重,升序 set<int,grea ...

  5. 非对称加密和linux上的 ssh-keygen 工具使用

    rsa :创造非对称加密的三个人名.原理是两个1024到2048之间的素数,以此为乘积.等... a*b=c  一般a*b为私钥端,c为公钥端.因为 c非常难算出a和b. ssh-keygen -t ...

  6. Centos 7 端口聚合

    简单粗暴,直接复制命令就好了 还是先啰嗦一下,添加网卡之后,如果没有网卡配置文件,可以通过nmcli con show 先查看网卡的唯一ID,然后复制其他的网卡配置文件,修改device项,name项 ...

  7. mysql 导入sql文件

    navicat 工具导入 1.连接数据库后,右键选择导入sql文件 2.选择sql文件,开始导入 4.过程图 5.结果图

  8. 前端需要了解的颜色模型,RGB、HSL和HSV

    颜色模型,是用来表示颜色的数学模型.比如最常见的 RGB模型,使用 红绿蓝 三色来表示颜色. 一般的颜色模型,可以按照如下分类: 面向硬件设备的颜色模型:RGB,CMYK,YCrCb. 面向视觉感知的 ...

  9. vue-router 4 你真的熟练吗?

    虽然 vue-router 4 大多数 API 保持不变,但是在 vue3 中以插件形式存在,所以在使用时有一定的变化.接下来就学习学习它是如何使用的. 一.安装并创建实例 安装最新版本的 vue-r ...

  10. 设计模式--策略模式Strategy

    策略模式 算法经常需要被改变==使用S 节省资源(很多if else if-.不会被执行,却会被装载到代码段) 动机 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到 ...