Remainder Problem(根号分治)

Educational Codeforces Round 71 (Rated for Div. 2)

F. Remainder Problem

题目链接


F. Remainder Problem

题意:

给你一个由 500000 500000 500000 个整数(编号从 1 1 1 500000 500000 500000 )组成的数组 a a a 。最初 a a a 中的所有元素都为零。

您必须对这个数组进行两种类型的查询:

  • 1 1 1 x x x y y y - a x a_x ax 增加 y y y
  • 2 2 2 x x x y y y - 计算 ∑ i ∈ R ( x , y ) a i \sum\limits_{i \in R(x, y)} a_i iR(x,y)ai ,其中 R ( x , y ) R(x, y) R(x,y) 是所有从 1 1 1 500000 500000 500000 有模 x x x 同余 y y y 的整数的集合。

你能处理所有的查询吗?

思路:

很好的一道根号分治题,类似的还有这道 F. Sum of Progression题解的F题

操作 1 1 1 好说,看操作 2 2 2 如何实现。发现其实就是求 ∑ a i ∗ x + y \sum a_{i*x+y} aix+y 形式的数,对这种形式,大佬说:
在这里插入图片描述

当步长 x x x 很大的时候,我们其实枚举不了几次就会到边界 n n n ,因此 x x x 很大的时候直接枚举。而当 x x x 比较小的时候,我们可以提前处理出结果。这里把起始位置为 j j j ,步长为 i i i 的这一系列位置上的数的和设为 s [ i ] [ j ] s[i][j] s[i][j],这样操作 1 1 1 修改的时候只需要枚举步长 i i i,给所有 s [ i ] [ x % i ] s[i][x\%i] s[i][x%i] y y y 就可以维护住了。

而这个分界点就取为 n \sqrt n n ,当 x ≥ n x\ge\sqrt n xn 时,就用第一种枚举求和的方式来算。否则就直接查询 s [ i ] [ j ] s[i][j] s[i][j]。这样每次暴力枚举的次数不会超过 n \sqrt n n ,而每次修改的时候维护 s s s 数组时枚举的步长也只需要枚举 n \sqrt n n 次。总的时间复杂度就是枚举的复杂度加上维护 s s s 数组的复杂度,总的复杂度不会超过 q n q\sqrt n qn

为什么分界点取为 n \sqrt n n ?证明只需要用均值不等式就可以了。最坏情况显然是要么操作 1 1 1 修改,要么操作 2 2 2 使用枚举方式查询答案。如果分界点选为 t t t 最优,假设操作 1 1 1 x x x 次,那么操作 2 2 2 q − x q-x qx 次。总的操作次数是 x ∗ t + ( q − x ) ∗ n t x*t+(q-x)*\frac nt xt+(qx)tn,因为算最坏的情况,因此需要 t = n t t=\frac nt t=tn,否则就会按着用时最多的操作 a l l   i n all\ in all in,因此就有了 t = n t=\sqrt n t=n ,总的时间复杂度就是 x ∗ n + ( q − x ) ∗ n = q ∗ n x*\sqrt n+(q-x)*\sqrt n=q*\sqrt n xn +(qx)n =qn

code:

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int n=5e5;
const int sqn=710;

int t,x,y;
ll a[n+5],s[sqn+5][sqn+5];//初值 步长

int main(){
	int _;
	cin>>_;
	while(_--){
		scanf("%d%d%d",&t,&x,&y);
		if(t&1){
			a[x]+=y;
			for(int i=1;i<sqn;i++)
				s[i][x%i]+=y;
		}
		else {
			if(x>=sqn){
				ll ans=0;
				for(int i=y;i<=n;i+=x)ans+=a[i];
				printf("%lld\n",ans);
			}
			else {
				printf("%lld\n",s[x][y]);
			}
		}
	} 
	return 0;
}
  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值