题目
将一个n,拆解成两个数a*b的乘积,
a和b都不能有平方因子,如,
则36就不能拆成12*3或3*12
顺序不同视为两种拆分方式,
f(n)定义为n的拆分方式的数目,
给定n<=2e7,求
思路来源
https://blog.youkuaiyun.com/Originum/article/details/82291901
题解
算每个a的贡献。
先用埃筛(O(nlognlon))处理出所有不含平方因子的数
然后用这些数两两配对去凑小于等于n的数,
对于某个数,二分一下和它相乘不超过n的最大的数,
直接枚举数对的第一个数的话是O(T*nlogn),不能满足要求
如果令第一个数小于第二个数的话就是O(T*sqrt(n)logn)
统计的时候,
自己*自己的只统计一次[i,i]
自己*比自己大的统计两次[i+1,r],r是相乘不超过n的上界
心得
在数组里二分找不大于n的最大的数,
可以lower_bound一下,然后看是否相等,
如果不等的话往回退一个
还是多写写朴素二分叭,基础很重要
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=2e7+10;
ll ok[maxn],cnt;
int t,n;
bool vis[maxn];
ll ans;
void init()
{
ok[cnt++]=1;
for(ll i=2;i<maxn;++i)//O(2e7*log(log(2e7)))
{
if(!vis[i])ok[cnt++]=i;
ll now=i*i;
for(ll j=now;j<maxn;j+=now)
vis[j]=1;
}
}
int main()
{
init();
scanf("%d",&t);
//for(int i=0;i<20;++i)printf("%d ",ok[i]);puts("");
while(t--)
{
ans=0;
scanf("%d",&n);
int l=0,r=cnt;
while(l<=r)
{
int mid=(l+r)>>1;
if(ok[mid]*ok[mid]>n)r=mid-1;//O(T*sqrt(n))
else l=mid+1;
}
for(int i=0;i<=r;++i)
{
ans++;//[i,i]
int L=0,R=cnt;
while(L<=R)
{
int mid=(L+R)>>1;
if(ok[i]*ok[mid]>n)R=mid-1;
else L=mid+1;
}
//printf("[0,%d]:%d\n",r,2*R);
ans+=2*(R-i);//i与[i+1,R]两次
}
printf("%lld\n",ans);
}
return 0;
}