分解质因数板子:
目录
题意:
给你个数组a,看你能找出多少种长度相同的数组b。
条件是前i个数的gcd等于ai
分析:
前 i-1个数的gcd就是ai-1了。
所以每个位置之间独立,即我们最终结果看每个位置有多少种,乘起来即可。
gcd (ai-1,bi) == ai ——> gcd ( ai-1/ai , bi/ai ) == 1
ai-1 / ai 已知。
我们要看bi有多少种,也就是看1 ~ bi/ai 有多少个符合。
设bi / ai 为 k,则
也就是看 1~k 有多少个与 ai-1 / ai 互质。
我们将ai - 1 / ai质因数分解来计算:
((最小的9个质因子)2*3*5*7*11*13*17*19*23 == 223092870 < 1e9)
含有质因子就不互质,总数k减去不互质的数目就是互质的数目。
(比如 c 与 ai-1 / ai 互质 ,那么2*c , 3*c也是互质的,所以k内就有k/c个。)
由于我们计算数目的时候是以↑的方式计算的,所以为了避免重复计算(比如质因子含有2的,和含有3的,以及同时含有2和3的),才需要用容斥。
说到这里应该很明确了。
参考代码:
long long fp(long long a, int c)
{
if (c == 0)return 1;
if (c == 1)return a % MOD;
long long tmp = fp(a, c / 2);
if (c % 2 == 0)
return tmp * tmp % MOD;
else
return tmp * tmp % MOD * a % MOD;
}
vector<int>divide(int x)
{
vector<int>ret;
for (int i = 2; i <= x / i; i++)//i*i <= x
{
if (x % i == 0)
{
ret.push_back(i);
while (x % i == 0)
x /= i;
}
}
if (x > 1)ret.push_back(x);
return ret;
}
void solve()
{
int n, m;
cin >> n >> m;
vector<int>arr(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
}
ll ans = 1;
for (int i = 2; i <= n; i++)
{
if (arr[i - 1] % arr[i] != 0)
{
ans = 0; break;
}
else
{
//求一个范围内,与数v互素的个数,我们可以用容斥来解决
int aim1 = arr[i - 1] /arr[i];
vector<int>ret = divide(aim1);
int k = m / arr[i];
int situ = 0;
for (int i = 1; i < fp(2,ret.size()); i++)
{
int multi = 1;
int cnt = 0;
for (int j = 1, cur = 1; j <= ret.size(); j++,cur<<=1)
{
if (cur & i)
{
multi *= ret[j-1];
cnt++;
}
}
if (cnt % 2)
situ += k / multi;
else
situ -= k / multi;
}
ans = ans * (k-situ) % MOD;
}
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}