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;
}