题意
M个石头围成圈(0-indexed),有N个青蛙,青蛙开始都在0处,每只青蛙可以每次跳,可以跳无限次,求至少被一个青蛙覆盖的石头id和
题解
对于每只青蛙,易知其跳过的石头为加法群中元素
即先处理得到
单个贡献为,即
之和
通过容斥可以得到
问题转变为求容斥系数
对于表示加入n个数对于M的因子d,正负系数(0/1)为多少
可以发现没加入一个, 容斥系数变化为继承、正负互换后贡献、单个贡献:
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
using LL = long long;
const int MAXN = 1e5 + 5;
int T, N, M;
int p[MAXN];
vector<int> fac;
int gcd(int a, int b);
int dp[1505][2][2], lcm[1505][1505];
unordered_map<int, int> MP;
int main() {
scanf("%d", &T);
for (int t = 1; t <= T; t++) {
scanf("%d%d", &N, &M);
fac.clear();
int P = M;
for (int i = 1; i * i <= P; i++)
if (P % i == 0) {
fac.push_back(i);
if (i != P / i) fac.push_back(P / i);
}
sort(fac.begin(), fac.end());
MP.clear();
for (int i = 0; i < fac.size(); i++) MP[fac[i]] = i;
for (int i = 0; i < fac.size(); i++)
for (int j = 0; j <= i; j++) {
int tmp;
tmp = 1LL * fac[i] * fac[j] / gcd(fac[i], fac[j]);
// cout << fac[i] << " " << fac[j] << " " << tmp << endl;
lcm[i][j] = lcm[j][i] = MP[tmp];
}
int now = 0, pre = 1;
for (int j = 0; j < fac.size(); j++) memset(dp[j], 0, sizeof(dp[j]));
for (int i = 1; i <= N; i++) {
scanf("%d", p + i);
int b = gcd(p[i], M);
int id = MP[b];
for (int j = 0; j < fac.size(); j++) {
dp[j][0][now] = dp[j][0][pre];
dp[j][1][now] = dp[j][1][pre];
}
dp[id][0][now] += 1;
for (int j = 0; j < fac.size(); j++) {
dp[lcm[id][j]][0][now] += dp[j][1][pre];
dp[lcm[id][j]][1][now] += dp[j][0][pre];
}
// for (int j = 0; j < fac.size(); j++)
// printf("%d: %d %d\n", fac[j], dp[j][0][now], dp[j][1][now]);
// printf("\n");
swap(now, pre);
}
LL ans = 0;
for (int j = 0; j < fac.size(); j++) {
int coffi = dp[j][0][pre] - dp[j][1][pre];
ans += 1LL * M / fac[j] * coffi;
}
ans = M * ans - M;
ans /= 2;
printf("Case #%d: %lld\n", t, ans);
}
return 0;
}
int gcd(int a, int b) { return (!b) ? a : gcd(b, a % b); }