卧薪尝胆,厚积薄发。
提高B组模拟 轨道
Date: Sun Aug 12 16:05:32 CST 2018
In Category:
NoCategory
Description:
$v=\frac{a_1\times a_2\times \cdots\times a_n}{k}$
已知
$n$
和
$k$
,求这
$n$
个正整数都不大于
$m$
的情况下有多少种选择方式使得
$v$
为与
$k$
互质正整数。
$1\le n\le 3000$
$1\le m\le 10^9$
$1\le k\le 10^7 $
Solution:
$DP$
,设状态为
$f[i][j]$
表示选了
$i$
个数乘积与
$k$
的最大公约数为
$k$
的第
$j$
个约数且乘积除以公约数与
$k$
互质的方案数,设
$a[i]$
为
$k$
的第
$i$
个约数,
$b[i]$
为
$i$
是
$k$
的第几个约数,转移方程为
$\begin{align}f[i][j]=\sum_{l=1}^j f[i-1][l]\times f[1][b[a[j]/a[l]]]\end{align}$
,预处理
$a[j]$
的约数转移。含义是选了
$i-1$
个数与
$k$
的最大公约数是
$k$
的第
$l$
个约数且乘积除以
$a[l]$
与
$k$
互质,则后面再选一个数使得与
$k$
的最大公约数是
$k$
的第
$j$
个约数且乘积除以公约数与
$k$
互质,如果不考虑乘积除以公约数与
$k$
互质,则所有使得含有一个约数
$a[p]$
且
$lcm(a[p],a[l])=a[j]$
的数字合法,但是由于要求互质,所以乘积除以
$k$
之后不能再有
$k$
的约数,所以只能是
$a[j]/a[l]$
,就能保证最后的最大公约数是
$a[j]$
且除掉后与
$k$
互质。现在的问题就变成了如何求
$f[1][l]$
即与
$k$
的最大公约数是
$k$
的第
$l$
个约数且除掉
$a[l]$
后与
$k$
互质的数的个数。即
$gcd(w,k)=a[l]$
,且
$gcd(\lfloor\frac w{a[l]}\rfloor,k)=1$
,发现满足第二个条件一定满足第一个条件,问题就变成了求
$1$
~
$\lfloor\frac m{a[l]}\rfloor$
中与
$k$
互质的数的个数,容斥原理做就可以了。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n,m,k;
#define MAXN 3010
#define MOD 10007
int a[MAXN],tot = 0;
int prime[MAXN],cnt = 0;
int f[MAXN][MAXN];
int res;
int b[10000010];
void dfs(int cur,int curlcm,int border,int type)
{
if(curlcm > border)return;
if(cur == 0)
{
res += type * (border / curlcm);
return;
}
if(curlcm * prime[cur] <= border)dfs(cur - 1,curlcm * prime[cur],border,-type);
dfs(cur - 1,curlcm,border,type);
return;
}
bool cmp(int x,int y){return x > y;}
inline void add(int &x,int y){x = x + y;x = (x >= MOD ? x - MOD : x);return;}
struct num
{
int v,nxt;
}p[1000010];
int pnum = 0;
int lin[MAXN] = {0};
inline void ins(int a,int b)
{
++pnum;p[pnum].v = b;p[pnum].nxt = lin[a];lin[a] = pnum;
return;
}
inline int read()
{
register int res = 0;
register char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))
{
res = (res << 1) + (res << 3) + (c ^ 48);
c = getchar();
}
return res;
}
int main()
{
freopen("orbits.in","r",stdin);
freopen("orbits.out","w",stdout);
n = read();m = read();k = read();
for(register int i = 1;i * i <= k;++i)if(k % i == 0)
{
a[++tot] = i;
if(i * i != k)a[++tot] = k / i;
}
register int s = k;
for(register int i = 2;i * i <= k;++i)
{
if(s % i == 0)prime[++cnt] = i;
while(s % i == 0)s /= i;
}
if(s != 1)prime[++cnt] = s;
sort(a + 1,a + 1 + tot);
for(register int i = 1;i <= tot;++i)b[a[i]] = i;
sort(prime + 1,prime + 1 + cnt,cmp);
for(register int i = 1;i <= tot;++i)
{
res = 0;
dfs(cnt,1,m / a[i],1);
f[1][i] = res % MOD;
}
for(register int j = 1;j <= tot;++j)
for(register int l = 1;a[l] * a[l] <= a[j];++l)
if(a[j] % a[l] == 0)
{
ins(j,l);
register int pp = a[j] / a[l];
if(pp != a[l])ins(j,b[pp]);
}
for(register int i = 2;i <= n;++i)
for(register int j = 1;j <= tot;++j)
for(register int l = lin[j];l != 0;l = p[l].nxt)
add(f[i][j],f[i - 1][p[l].v] * f[1][b[a[j] / a[p[l].v]]] % MOD);
printf("%d\n",f[n][tot]);
fclose(stdin);
fclose(stdout);
return 0;
}
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡