bzoj3529 [Sdoi2014]数表(反演)

题目链接

分析:
题目所求:

i=1nj=1mh(i,j)[h(i,j)<=a],h(i,j)=ij ∑ i = 1 n ∑ j = 1 m h ( i , j ) [ h ( i , j ) <= a ] , h ( i , j ) = 能 同 时 整 除 i 和 j 的 所 有 自 然 数 之 和

实际上, h h 的意义:gcd(i,j)的约数和
f(n) f ( n ) n n 的约数和

随意查了一下,约数和定理:
n=p1a1p2a2p3a3pkak
f(n)=(p00+p11+pa11)(p02+p12+pa22)(p0k+p1k+pakk) f ( n ) = ( p 0 0 + p 1 1 + … p 1 a 1 ) ( p 2 0 + p 2 1 + … p 2 a 2 ) … ( p k 0 + p k 1 + … p k a k )

我们发现当 p,q p , q 互质时, f(pq)=f(p)f(q) f ( p q ) = f ( p ) f ( q ) f f 是个积性函数
也就是说f可以线性筛求解(当然直接暴力求也没什么影响)

a a 的限制比较复杂,简单的,我们先来看看如果没有a的限制,是个什么情况

k=1min(n,m)i=1nj=1mf(k)[gcd(i,j)=k] ∑ k = 1 m i n ( n , m ) ∑ i = 1 n ∑ j = 1 m f ( k ) [ g c d ( i , j ) = k ]

g(i) g ( i ) 表示 nx=1my=1[gcd(x,y)=i] ∑ x = 1 n ∑ y = 1 m [ g c d ( x , y ) = i ]

g(i)=d=1min(n/i,m/i)ndimdiμ(d) g ( i ) = ∑ d = 1 m i n ( n / i , m / i ) n d i m d i μ ( d )

则原式:

k=1min(n,m)f(k)d=1min(n/k,m/k)ndkmdkμ(d) ∑ k = 1 m i n ( n , m ) f ( k ) ∑ d = 1 m i n ( n / k , m / k ) n d k m d k μ ( d )

Q=dk Q = d k

k=1min(n,m)f(k)Q=1min(n,m)nQmQμ(Qk)=Q=1min(n,m)nQmQk|Qf(k)μ(Qk) ∑ k = 1 m i n ( n , m ) f ( k ) ∑ Q = 1 m i n ( n , m ) n Q m Q μ ( Q k ) = ∑ Q = 1 m i n ( n , m ) n Q m Q ∑ k | Q f ( k ) μ ( Q k )

显然 k|Qf(k)μ(Qk) ∑ k | Q f ( k ) μ ( Q k ) f f 函数与μ函数的狄利克雷卷积
预处理前缀和,前面的 nQmQ n Q m Q 分块,那么这个式子的计算就达到了 n n

那么我们再加上 a a 的限制
离线,将询问按照a从小到大排序, f f 函数按照从小到大排序
询问的时候,我们就动态维护一个树状数组c
每次处理询问之前,把数值小于当前a的 f f 值加入树状数组
树状数组的每一位c[x]存的 d|xf(d)μ(xd) ∑ d | x f ( d ) μ ( x d )
数据组数为 T T ,询问复杂度O(Tnlogn)

注意:

这题复杂度有点卡,对于取模运算我们可以对 int i n t 自然溢出,最后再 &2147483647 & 2147483647 就可以了

tip

PoPoQQQ大大的题解

第一次写完,发现小数据OK,大数据WA了(比标准答案大一点)、
竟然是线性筛mu写错了。。。orz
注意最后的取模,一不小心就会炸掉变成负数

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N=100005;
int n,m,Q,sshu[N],tot=0,mu[N],ans[N],mx,c[N];
bool no[N];
struct node{
    int n,m,A,id;
    bool operator <(const node &b) const {
        return A<b.A;
    }
};
node a[N];
struct node2{
    int x,id;
    bool operator <(const node2 &b) const {
        return x<b.x||(x==b.x&&id<b.id);
    }
};
node2 f[N];

void prepare(int n) {
    memset(no,0,sizeof(no));
    mu[1]=1;
    for (int i=2;i<=n;i++) {
        if (!no[i]) {
            sshu[++tot]=i;
            mu[i]=-1; 
        }
        for (int j=1;j<=tot&&sshu[j]*i<=n;j++) {
            no[i*sshu[j]]=1;
            if (i%sshu[j]==0) {
                mu[i*sshu[j]]=0;
                break;
            }
            mu[i*sshu[j]]=-mu[i];
        }
    }
    for (int i=1;i<=n;i++)        //暴力求f 
        for (int j=i;j<=n;j+=i)
            f[j].x+=i;
    for (int i=1;i<=n;i++) f[i].id=i;
}

void add(int x,int z) {for (int i=x;i<=mx;i+=(i&(-i))) c[i]+=z;}
int ask(int x) {int ans=0; for (int i=x;i>0;i-=(i&(-i))) ans+=c[i]; return ans;} 

void solve(int _) {
    int n=a[_].n,m=a[_].m;
    int last=0;
    for (int i=1;i<=min(n,m);i=last+1) {
        last=min(n/(n/i),m/(m/i));
        ans[a[_].id]+=(n/i)*(m/i)*(ask(last)-ask(i-1));
    }
}

int main()
{
    mx=0;
    scanf("%d",&Q);
    for (int i=1;i<=Q;i++) {
        scanf("%d%d%d",&a[i].n,&a[i].m,&a[i].A);
        a[i].id=i; mx=max(mx,min(a[i].n,a[i].m));
    }
    prepare(mx);
    sort(a+1,a+1+Q);
    sort(f+1,f+1+mx);
    int now=1;
    for (int i=1;i<=Q;i++) {
        while (now<=mx&&f[now].x<=a[i].A) {
            for (int j=f[now].id;j<=mx;j+=f[now].id)   //sum(k|Q) f(k)mu(Q/k) 所有k的倍数Q都需要add 
                add(j,f[now].x*mu[j/f[now].id]);       //卷积 
            now++;
        }
        solve(i);
    }
    for (int i=1;i<=Q;i++) printf("%d\n",ans[i]&((1<<31)-1));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值