HDU4947-莫比乌斯反演在序列问题上的应用

题目大意:

给你一个长度为 l l l的数组, q q q次操作:

1 1 1 让每个满足 g c d ( i , n ) = d gcd(i,n)=d gcd(i,n)=d的位置 i i i上加 v v v.
2 2 2 查询前缀和 [ 1 , x ] [1,x] [1,x].

题目思路:
修改:

考虑对于每个 i i i有式子:
a i = v ∗ [ g c d ( i , n ) = d ] a_i=v*[gcd(i,n)=d] ai=v[gcd(i,n)=d]
a i = v ∗ [ g c d ( i d , n d ) = 1 ] a_i=v*[gcd(\frac id,\frac nd)=1] ai=v[gcd(di,dn)=1]
= ∑ p ∣ g c d ( i d , n d ) v ∗ μ ( p ) =\sum_{p|gcd(\frac id,\frac nd)}^{}v*\mu(p) =pgcd(di,dn)vμ(p)
= ∑ p ∣ i d , p ∣ n d v ∗ μ ( p ) =\sum_{p|\frac id,p| \frac nd}^{}v*\mu(p) =pdi,pdnvμ(p)
= ∑ p d ∣ i , p ∣ n d v ∗ μ ( p ) =\sum_{pd|\frac i,p| \frac nd}^{}v*\mu(p) =pd,ipdnvμ(p)

由于 n d \frac nd dn是已知量,所以我们可以枚举 p ∣ n d p|\frac nd pdn.对于每个确定的 p p p.
我们构造 f p d + = v ∗ μ ( p ) f_{pd}+=v*\mu(p) fpd+=vμ(p)

那么这个时候 a i = ∑ j ∣ i f j a_i=\sum_{j|i}^{}f_j ai=jifj


查询:

∑ i = 1 x a i = ∑ i = 1 x ∑ j ∣ i f j = ∑ j = 1 x f j ∗ ⌊ x j ⌋ \sum_{i=1}^{x}a_i=\sum_{i=1}^{x}\sum_{j|i}^{}f_j=\sum_{j=1}^{x}f_j*\lfloor \frac xj \rfloor i=1xai=i=1xjifj=j=1xfjjx

分块+树状数组维护即可.

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vll vector<ll>
#define fi first
#define se second
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
int u[maxn] , id[maxn] , bk[maxn] , p[maxn] , cnt;
ll sum[maxn];
int l;
ll lowbit(ll x){return x & -x;}
void add(ll x,ll c){while(x<=l){sum[x] += c;x += lowbit(x);}}
ll ask(ll x){ll ans=0;while(x){ans+=sum[x];x-=lowbit(x);}return ans;}
vector<int> f[maxn];
void init ()
{
    u[1] = 1;
    for (int i = 2 ; i < maxn ; i++){
        if (!bk[i]){
            p[++cnt] = i;
            u[i] = -1;
        }
        for (int j = 1 ; j <= cnt && i * p[j] < maxn ; j++){
            bk[i * p[j]] = 1;
            if (i % p[j] == 0) {
                u[i * p[j]] = 0;
                break;
            }else {
                u[i * p[j]] = -u[i];
            }
        }
    }
    for (int i = 1 ; i < maxn ; i++)
        for (int j = i ; j < maxn ; j += i)
            f[j].pb(i);
    return ;
}
int main()
{
    ios::sync_with_stdio(false);
    init();
    int q , cnt = 0;
    while (cin >> l >> q , l || q){
        for (int i = 1 ; i <= l ; i++) sum[i] = 0;
        cout << "Case #" << (++cnt) << ":" << endl;
        for (int i = 1 ; i <= q ; i++){
            int op; cin >> op;
            if (op == 1){
                int n , d , v; cin >> n >> d >> v;
                if (n % d) continue;
                for (auto p : f[n / d]) add(p * d , 1ll * v * u[p]);
            }else {
                int x; cin >> x;
                ll ans = 0;
                for (int ls = 1 , r ; ls <= x ; ls = r + 1){
                    r = x / (x / ls);
                    ll a = ask(r) - ask(ls - 1);
                    ans += a * (x / ls);
                }
                cout << ans << endl;
            }
        }

    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值