Harbour.Space Scholarship Contest 2021-2022 ( Div. 1 + Div. 2) F. Pairwise(树状数组)

题意:

给出一个数组 a a a​ ,定义 f ( k ) = ∑ i , j ≤ k a i % a j f(k)=\sum\limits_{i,j \leq k} a_i\% a_j f(k)=i,jkai%aj​。 对于 k k k​从1到 n n n​ ,输出 f ( k ) f(k) f(k)

题解:

首先, k k k变到 k + 1 k+1 k+1的过程中,增加了数 a k + 1 a_{k+1} ak+1 ,那么只需在 f ( k ) f(k) f(k)的基础上,再去计算 a k + 1 a_{k+1} ak+1的贡献即可。

那么怎么计算这个数的贡献呢?分两种情况计算。

1. 1. 1. a k + 1 a_{k+1} ak+1 放后面时,我们利用树状数组找出比 a k + 1 a_{k+1} ak+1 小的数,那么这些数对 a k + 1 a_{k+1} ak+1取模还是自己本身,所以求这些数的总和即可。对于比 a k + 1 a_{k+1} ak+1 大的数,我们可以枚举区间 [ t a k + 1 , ( t + 1 ) a k + 1 − 1 ] [ta_{k+1},(t+1)a_{k+1}-1] [tak+1,(t+1)ak+11] ,计算出这个区间里面有多少个数,记为 c n t cnt cnt,同时也计算出这个区间内的数的总和 s u m sum sum ,那么这个区间里面的数对 a k + 1 a_{k+1} ak+1 取模的答案就是 s u m − c n t ⋅ t a k + 1 sum-cnt \cdot ta_{k+1} sumcnttak+1

2. 2. 2.​​ 当 a k + 1 a_{k+1} ak+1​​放前面时,也可以利用树状数组求出大于 a k + 1 a_{k+1} ak+1​​ 的数的个数,记为 c n t cnt cnt,那么 a k + 1 a_{k+1} ak+1 对这些数取模还是 a k + 1 a_{k+1} ak+1 ,答案就是 c n t ⋅ a k + 1 cnt \cdot a_{k+1} cntak+1 。对于比 a k + 1 a_{k+1} ak+1 小的数,记为 x x x ,我们可以先对这些数预先做以下操作: t r e e [ t ⋅ x ] + = x tree[t \cdot x]+=x tree[tx]+=x

然后再查询出前缀 s u m ( t r e e [ a k + 1 ] ) sum(tree[a_{k+1}]) sum(tree[ak+1]) ,假设比 a k + 1 a_{k+1} ak+1小的数有 y y y个 那么答案就是 y ⋅ a k + 1 − s u m ( t r e e [ a k + 1 ] ) y \cdot a_{k+1} -sum(tree[a_{k+1}]) yak+1sum(tree[ak+1])​ 。

例如 :对于 1 2 5 ,1现在1 2 3 4 5所有位置加上了1 ,2在2 4 位置加上了2 ,那么5%1+5%2就可以变成 5-(1+1+1+1+1)+5-(2+2) 。其实就是预先求出 x / y ⋅ y x/y \cdot y x/yy ,那么模数只要相减即可。

代码:

#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int MAXN=3e5+5;
const int inf=0x3f3f3f3f;
ll tree[MAXN][3];
int a[MAXN];
int n=300000;
int lowbit(int i)
{
    return i&-i;
}
ll sum(int i,int id)
{
    ll s=0;
    while(i>0)
    {
        s+=tree[i][id];
        i-=lowbit(i);
    }
    return s;
}
void add(int i,int val,int id)
{
    while(i<=n)
    {
        tree[i][id]+=val;
        i+=lowbit(i);
    }
}
int main()
{
    int m;
    cin>>m;
    ll ans=0;
    for(int i=1;i<=m;i++)
    {
        cin>>a[i];
        ans+=sum(a[i]-1,1);
        ans+=1ll*(sum(n,0)-sum(a[i],0))*a[i];
        for(int j=2*a[i];j<=n;j+=a[i])
        {
            ans+=(sum(j-1,1)-sum(j-a[i],1))-1ll*(sum(j-1,0)-sum(j-a[i],0))*(j-a[i]);
            if(j==n) break;
            if(j+a[i]>n)
            {
                ans+=(sum(n,1)-sum(j,1))-1ll*(sum(n,0)-sum(j,0))*(j);
                break;
            }
        }
        if(2*a[i]>n)
        {
            ans+=(sum(n,1)-sum(a[i],1))-1ll*(sum(n,0)-sum(a[i],0))*(a[i]);
        }
        ans+=1ll*sum(a[i]-1,0)*a[i]-sum(a[i],2);
        for(int j=a[i];j<=n;j+=a[i])
        {
            add(j,a[i],2);
        }
        add(a[i],a[i],1);
        add(a[i],1,0);
        cout<<ans<<" ";
    }
}
posted @ 2021-07-23 11:37  TheBestQAQ  阅读(46)  评论(0)    收藏  举报