1019 华华开始学信息学 数学思维 树状数组 根据数据范围分块讨论

 链接:https://ac.nowcoder.com/acm/contest/26896/1019
来源:牛客网

题目描述

因为上次在月月面前丢人了,所以华华决定开始学信息学。十分钟后,他就开始学树状数组了。这是一道树状数组的入门题:
给定一个长度为N的序列A,所有元素初值为0。接下来有M次操作或询问:
操作:输入格式:1 D K,将ADA_DAD加上K。
询问:输入格式:2 L R,询问区间和,即∑i=LRAi\sum_{i=L}^{R}A_ii=LRAi
华华很快就学会了树状数组并通过了这道题。月月也很喜欢树状数组,于是给华华出了一道进阶题:
给定一个长度为N的序列A,所有元素初值为0。接下来有M次操作或询问:
操作:输入格式:1 D K,对于所有满足1≤i≤N1\le i\le N1iN且i≡0(mod  D)i\equiv0(\mod D)i0(modD)的i,将AiA_iAi加上K。
询问:输入格式:2 L R,询问区间和,即∑i=LRAi\sum_{i=L}^{R}A_ii=LRAi
华华是个newbie,怎么可能会这样的题?不过你应该会吧。

输入描述:

第一行两个正整数N、M表示序列的长度和操作询问的总次数。
接下来M行每行三个正整数,表示一个操作或询问。

输出描述:

对于每个询问,输出一个非负整数表示答案。
示例1

输入

复制
10 6
1 1 1
2 4 6
1 3 2
2 5 7
1 6 10
2 1 10

输出

复制
3
5
26

备注:

1≤N,M≤1051\le N,M\le10^51N,M105,1≤D≤N1\le D\le N1DN,1≤L≤R≤N1\le L\le R\le N1LRN,1≤K≤1081\le K \le 10^81K108

分析

这是道要根据操作时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;
}

/*样例区


*/

//------------------------------------------------------------

 

posted @ 2022-08-10 20:31  er007  阅读(54)  评论(0编辑  收藏  举报