hdu4746 Mophues(莫比乌斯反演)-好题

31 篇文章 0 订阅
14 篇文章 0 订阅

hdu 1695
http://blog.csdn.net/abc13068938939/article/details/52198163
BZOJ 2301
http://blog.csdn.net/abc13068938939/article/details/52201090
BZOJ 2818
http://blog.csdn.net/abc13068938939/article/details/52202025
上面这三道题是这道题的简单版本,这道题的思路和BZOJ 2818的答题思路是一样的,但是难点在于求出那个前缀和(具体看下面),由于多了p限制,所以不妨先求出限制为=p(原先是<=p),然后再前缀和,
于是可以枚举公约数i,利用筛法找出i的倍数j,i对j的贡献系数为:F(num(i))(j)+=μ(j/i)
具体代码

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <stack>
#include <vector>
#include <string.h>
#include <queue>
#define msc(X) memset(X,-1,sizeof(X))
#define ms(X) memset(X,0,sizeof(X))
typedef long long LL;
using namespace std;
const int MAXN=5e5;
int F[20][MAXN+10],h[MAXN+10],num[MAXN+10];
int mu(int n)
{
    if(h[n]==-1) return 0;
    else if(h[n]&1) return -1;
    else return 1;
}
void inti(void)
{
    ms(F);
    ms(h);
    ms(num);
    for(int i=2;i<MAXN;i++)
        if(!num[i])
            for(int j=i;j<MAXN;j+=i)
            {
                int tmp=j,tct=0;
                while(tmp%i==0) tmp/=i,tct++;
                num[j]+=tct;
                if(tct>1) h[j]=-1;
                else if(h[j]>=0) ++h[j];
            }
    for(int i=1;i<MAXN;i++)
        for(int j=i;j<MAXN;j+=i)
            F[num[i]][j]+=mu(j/i);
    for(int i=1;i<MAXN;i++)
        for(int j=1;j<19;j++)
            F[j][i]+=F[j-1][i];
    for(int i=1;i<MAXN;i++)
        for(int j=0;j<19;j++)
            F[j][i]+=F[j][i-1];
}
int main(int argc, char const *argv[])
{
    int q;
    inti();
    cin>>q;
    while(q--){
        int n,m,p;
        scanf("%d %d %d",&n,&m,&p);
        if(p>18||(1<<p)>=max(n,m)) {printf("%I64d\n",(LL)n*m );continue;}
        LL res=0;
        int *P=F[p];
        for(int i=1,r;i<=n&&i<=m;i=r+1)
        {
            int d1=n/i,d2=m/i;
            r=min(n/d1,m/d2);
            res+=(LL)(P[r]-P[i-1])*d1*d2;
        }
        printf("%I64d\n",res );
    }
    return 0;
}

还有一种就是直接求tn[i][j}=
Sigma(d|j&&cnt(d)<=i) u(d)
// 其中cnt(d)表示d的因子个数
不过这一部分代码到现在都没看懂

两种写法耗时差不多

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <stack>
#include <vector>
#include <string.h>
#include <queue>
#define msc(X) memset(X,-1,sizeof(X))
#define ms(X) memset(X,0,sizeof(X))
typedef long long LL;
using namespace std;
const int MAXN=500009;
int p,tot,prime[MAXN/3],cnt[MAXN+10];
int tn[20][MAXN+10];
void inti(void)//预处理cnt,prime,tn
{
    ms(cnt);
    ms(tn);
    tot=0;
    for(int i=2;i<=MAXN;i++)
    {
        if(!cnt[i]) prime[tot++]=i,cnt[i]=1;
        for(int j=0;j<tot;j++)
        {
            if(i*prime[j]>MAXN) break;
            cnt[i*prime[j]]=cnt[i]+1;
            if(i%prime[j]==0) break;
        }
    }
    for(int i=0;i<19;i++)
    {
        tn[i][1]=1;
        for(int j=2;j<=MAXN;j++)
        {
            if(cnt[j]-i==1){
                for(int k=j;k<=MAXN;k+=j)
                    tn[i][k]--;
            }
            else if(cnt[j]>i){
                int tmp=-1-(tn[i][j]);
                tn[i][j]=tmp;
                if(tmp){
                    for(int k=j+j;k<=MAXN;k+=j)
                        tn[i][k]+=tmp;
                }
            }
        }
        // tn[i][j]表示Sigma(d|j&&cnt(d)<=i) u(d)  
        // 其中cnt(d)表示d的因子个数
        // 之后求前缀和
        for(int j=2;j<=MAXN;j++)
            tn[i][j]+=tn[i][j-1];
    }
}
int main(int argc, char const *argv[])
{
    int q;
    cin>>q;
    inti();
    while(q--){
        int n,m;
        scanf("%d %d %d",&n,&m,&p);
        if(p>=19||(1<<p)>=max(n,m)) {printf("%I64d\n",(LL)n*m );continue;}
        LL res=0;
        int *P=tn[p];
        for(int i=1,r;i<=n&&i<=m;i=r+1)
        {
            int dx=n/i,dy=m/i;
            r=min(n/dx,m/dy);
            res+=(LL)(P[r]-P[i-1])*dx*dy;
        }
        printf("%I64d\n",res );
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值