1019 华华开始学信息学 数学思维 树状数组 根据数据范围分块讨论
链接:https://ac.nowcoder.com/acm/contest/26896/1019
来源:牛客网
题目描述
因为上次在月月面前丢人了,所以华华决定开始学信息学。十分钟后,他就开始学树状数组了。这是一道树状数组的入门题:
给定一个长度为N的序列A,所有元素初值为0。接下来有M次操作或询问:
操作:输入格式:1 D K,对于所有满足1≤i≤N1\le i\le N1≤i≤N且i≡0(mod D)i\equiv0(\mod D)i≡0(modD)的i,将AiA_iAi加上K。
询问:输入格式:2 L R,询问区间和,即∑i=LRAi\sum_{i=L}^{R}A_i∑i=LRAi。
华华是个newbie,怎么可能会这样的题?不过你应该会吧。
给定一个长度为N的序列A,所有元素初值为0。接下来有M次操作或询问:华华很快就学会了树状数组并通过了这道题。月月也很喜欢树状数组,于是给华华出了一道进阶题:
操作:输入格式:1 D K,将ADA_DAD加上K。
询问:输入格式:2 L R,询问区间和,即∑i=LRAi\sum_{i=L}^{R}A_i∑i=LRAi。
给定一个长度为N的序列A,所有元素初值为0。接下来有M次操作或询问:
操作:输入格式:1 D K,对于所有满足1≤i≤N1\le i\le N1≤i≤N且i≡0(mod D)i\equiv0(\mod D)i≡0(modD)的i,将AiA_iAi加上K。
询问:输入格式:2 L R,询问区间和,即∑i=LRAi\sum_{i=L}^{R}A_i∑i=LRAi。
华华是个newbie,怎么可能会这样的题?不过你应该会吧。
输入描述:
第一行两个正整数N、M表示序列的长度和操作询问的总次数。
接下来M行每行三个正整数,表示一个操作或询问。
输出描述:
对于每个询问,输出一个非负整数表示答案。
备注:
1≤N,M≤1051\le N,M\le10^51≤N,M≤105,1≤D≤N1\le D\le N1≤D≤N,1≤L≤R≤N1\le L\le R\le N1≤L≤R≤N,1≤K≤1081\le K \le 10^81≤K≤108
分析
这是道要根据操作时D的大小分情况讨论的题
对于每个数,它在范围 [L,R] 内D的倍数的个数是 R / D - (L-1) / D
D的所有倍数加上了K 求和 等价于 (R / D - (L-1)/ D)* k
所以只要让数组在D这个位置上加 k 就可以了
但是n的范围是 1e5,这样操作查询的复杂度也会变成 1e5,操作次数m 是1e5,1e5 *1e5 = 1e10
所以这种修改才做只能适用于D比较小的情况 以减小查询复杂度
在D比较大的时候,范围内D 的倍数也会变少,可以用树状数组,枚举所有D 的倍数,优化复杂度,修改操作差不多是 (N / D) * log(N),查询是log(N)
但是怎么界定D的范围呢?
第一种操作复杂度是D * m , 不妨令其变成 m * D * log(N)
两者相加就是 m * (D + (N/D) * log(N)
当D 等于 sqrt(N) 的时候 D + (N/D)最小 。总复杂度是 m * sqrt(N) * log(N) 差不多是1e8左右。
//-------------------------代码---------------------------- #define int ll const int N = 1e5+10; int n,m; int lazy[N]; int tr[N]; void add(int x,int k) { for(int i = x;i<N;i+=lowbit(i)) { tr[i] += k; } } int sum(int x) { int res = 0;for(int i = x;i;i-=lowbit(i)) { res += tr[i]; }return res; } void solve() { cin>>n>>m; while(m--) { int op;cin>>op; if(op == 1) { int d,k;cin>>d>>k; if(d <= sqrt(n)) { lazy[d] += k; } else { for(int i = d;i<N;i+=d) { add(i,k); } } } else { int l,r;cin>>l>>r; int res = sum(r) - sum(l-1); for(int i = 1;i<=sqrt(n);i++) { res += (r/i - (l-1)/i) * lazy[i]; } cout<<res<<endl; } } } void main_init() {} signed main(){ AC();clapping();TLE; cout<<fixed<<setprecision(12); main_init(); // while(cin>>n,n) // while(cin>>n>>m,n,m) // int t;cin>>t;while(t -- ) solve(); // {solve(); } return 0; } /*样例区 */ //------------------------------------------------------------