2440: [中山市选2011]完全平方数
最直观的想法是容斥,x-x/(pi)^2+x/(pi*pj)^2……
但是最坏情况下可能有O(2^p) ,虽然很快就会超过x,还是很快的,
换一种方法来分析,我们只要某些质数的乘积来算,那么mu[i]!=0的i就是符合要求的数字,而其他的+-号由于mu[i]相同,所以直接ans+=x/(i*i)*mu[i]即可.
3529: [Sdoi2014]数表
这道题目有点难度
http://www.cnblogs.com/Bleacher/p/6773761.html
贴一发lzw题解
我只要讲讲没讲清楚的
首先如何求F(x):x的约数和
我记录了val[x]:x的最小质数的乘积,即p1^c1
sum[x]:p1^0+p1^1+p1^2+……p1^c1
这这样就可以推导出F(x)了
然后
f[x]=∑d是i约数F[d]∗u[x/d]
这个我们可以枚举倍数暴力求,也就O(nlgn)
现在有了限制a,我们离线将a,f排序,每次线扫把符合条件的插入树状数组,然后照常
时间复杂度:插入O(nlogn^2) 查询 O(Qlgn
n‾√
)
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100000+1000;
int sum[N],mu[N],val[N],F[N],prime[N],c[N],ans[N],tot;
bool flag[N];
struct node{
int f,id;
bool operator < (const node &a) const
{return f<a.f;}
}f[N];
struct Que{
int n,m,a,id;
bool operator < (const Que &b) const
{return a<b.a;}
}a[20010];
void prepare()
{
mu[1]=F[1]=1;
for (int i=2;i<N;i++)
{
if (!flag[i])
{
prime[++tot]=i;
mu[i]=-1;
val[i]=i;
F[i]=sum[i]=i+1;
}
for (int j=1;j<=tot;j++)
{
int p=prime[j];
if (p*i>=N) break;
flag[p*i]=true;
if (i%p==0)
{
val[p*i]=val[i]*p;
sum[p*i]=sum[i]+val[p*i];
F[p*i]=F[i]/sum[i]*sum[i*p];
break;
}
mu[i*p]=-mu[i];
val[p*i]=p;
sum[p*i]=p+1;
F[p*i]=F[i]*(p+1);
}
}
for (int i=1;i<N;i++) f[i]=(node){F[i],i};
}
void add(int x,int v)
{
for (int i=x;i<N;i+=i&(-i)) c[i]+=v;
}
int query(int x)
{
int ret=0;
for (int i=x;i;i-=i&(-i)) ret+=c[i];
return ret;
}
void insert(int x,int d)
{
for (int i=d;i<N;i+=d)
add(i,x*mu[i/d]);
}
int main()
{
prepare();
int T;
scanf("%d",&T);
for (int i=1;i<=T;i++)
scanf("%d%d%d",&a[i].n,&a[i].m,&a[i].a),a[i].id=i;
sort(f+1,f+N);
sort(a+1,a+1+T);
int pos=1;
for (int i=1;i<=T;i++)
{
while (pos<N && f[pos].f<=a[i].a)
insert(f[pos].f,f[pos].id),pos++;
int n=a[i].n,m=a[i].m;
if (n>m) swap(n,m);
for (int j=1,last=0;j<=n;j=last+1)
{
last=min(n/(n/j),m/(m/j));
ans[a[i].id]+=(n/j)*(m/j)*(query(last)-query(j-1));
}
}
for (int i=1;i<=T;++i)
printf("%d\n",(ans[i]<0?ans[i]+2147483648:ans[i]));
return 0;
}