卧薪尝胆,厚积薄发。
签到
Date: Sat Jan 12 11:38:10 CST 2019
In Category:
NoCategory
Description:
求
$1\sim n$
中有多少数字满足以下三个条件之一:是
$b$
的倍数,是
$m$
个质数中任意一个的倍数,
$f(x)=1$
$f(x)$
的定义如下:
$f$
是积性函数,且
$f(1)=1$
,
$f(p^k)=r_k$
。
$1\leqslant n\leqslant 10^{10}$
## Solution:
首先是比较显然的
$\min\_25$
筛,考虑递归处理的那部分,我们实际上是枚举了所有数的最小质因子以及它的出现次数,然后统计已经被消成质数的数的贡献,那么整体来看实际上是枚举了一个数除了大于
$\sqrt n$
的质因子之外的其他质因子计算他们的贡献,然后对大于
$\sqrt n$
的质因子预处理
$g(n,j)$
计算,这个过程可以看作是从小的质因子向大的质因子进行数位
$DP$
的过程,那么我们也可以用类似数位
$DP$
的方法做。设
$S(n,j,nr,v,rem)$
表示在
$[1,n]$
中,
$minp\geqslant p_j$
,前面所有质因子的贡献的
$r$
的乘积为
$nr$
,前面的质因子是否出现过
$a$
中的数
$v$
,还要再乘
$rem$
才能是
$b$
的倍数,然后转移的时候和
$\min\_25$
筛一样,先是计算质数的贡献,这个需要预处理一个质数个数以及质数的和,然后再枚举最小质因子以及它的指数,考虑质数的贡献,如果
$f\ne 0||v||rem==1$
,那么说明已经满足条件了,所有的大于等于
$p_j$
的质数都合法,贡献就是
$g(n,|p|)-(j-1)$
,否则的话贡献就是所有大于等于
$p_j$
的给出来的质数的个数,如果
$rem$
是质数(合数肯定不行),并且满足
$rem$
在合法范围
$[prime[j],n]$
之间,那么也就是说可以选这个质数作为最后一个质数,那么还要加上
$1$
,注意如果
$rem$
也是
$m$
个数之一就不加一。这道题也压根就没有什么积性函数,但是他巧妙地运用了
$\min\_25$
筛法对素因子进行统计。
对
$rem$
可以暴力判断是不是质数,反正
$rem$
只有
$\sigma_0(b)$
个。
Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m,b;
#define MAXN 1000010
int r[65];
ll a[MAXN];
ll N;
int id1[MAXN],id2[MAXN];
int& id(ll k){return (k <= N ? id1[k] : id2[n / k]);}
ll w[MAXN];
ll g[MAXN];
bool isprime[MAXN];
int prime[MAXN],tot = 0;
void sieve(int n)
{
for(int i = 2;i <= n;++i)isprime[i] = true;
for(int i = 2;i <= n;++i)
{
if(isprime[i])prime[++tot] = i;
for(int j = 1;j <= tot && i * prime[j] <= n;++j)
{
isprime[i * prime[j]] = false;
if(i % prime[j] == 0)break;
}
}
return;
}
bool mark[MAXN];
ll sg[MAXN];
int pos[MAXN];
ll gcd(ll a,ll b){return (b == 0 ? a : gcd(b,a % b));}
map<int,bool> pr;
bool check(ll v)
{
if(pr.find(v) != pr.end())return pr[v];
if(v == 1)return false;
for(int i = 1;1ll * prime[i] * prime[i] <= v;++i)
{
if(v % prime[i] == 0)return (pr[v] = false);
}
return (pr[v] = true);
}
ll S(ll n,int j,int nr,int v,ll rem)
{
ll res = 0;
if(v || rem == 1 || nr * r[1] != 0)res = g[id(n)] - (j - 1);
else
{
res = pos[id(n)] - sg[j - 1];
if(check(rem) && rem <= n && rem >= prime[j] && !(rem <= N && mark[rem]))++res;
}
if(j > tot)return res;
for(int k = j;k <= tot && 1ll * prime[k] * prime[k] <= n;++k)
{
ll p1 = prime[k],p2 = 1ll * prime[k] * prime[k];
for(int e = 1;p2 <= n;++e,p1 = p2,p2 *= prime[k])
{
res += S(n / p1,k + 1,nr * r[e],v | mark[prime[k]],rem / gcd(rem,p1));
res += ((nr * r[e + 1] != 0) || (v || mark[prime[k]]) || (p2 % rem == 0));
}
}
return res;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&b);
for(int i = 1;i <= m;++i)scanf("%lld",&a[i]);
for(int i = 1;i <= 60;++i)scanf("%d",&r[i]);
N = sqrt(n);
for(int i = 1;i <= m;++i)if(a[i] <= N)mark[a[i]] = true;
sieve(N);
for(ll l = 1,r;l <= n;l = r + 1)
{
r = n / (n / l);
w[++w[0]] = n / l;
id(n / l) = w[0];
g[w[0]] = w[w[0]] - 1;
}
for(int j = 1;j <= tot;++j)
{
for(int i = 1;i <= w[0] && 1ll * prime[j] * prime[j] <= w[i];++i)
{
g[i] = g[i] - g[id(w[i] / prime[j])] + j - 1;
}
}
for(int i = 1;i <= tot;++i)sg[i] = sg[i - 1] + mark[prime[i]];
for(int i = 1;i <= w[0];++i)pos[i] = upper_bound(a + 1,a + 1 + m,w[i]) - a - 1;
cout << n - S(n,1,1,0,b) - 1 << endl;
return 0;
}
In tag:
数学-min_25筛
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡