一些 counting 实用技巧
求一个集合内有多少个数与给定的数 \(x\) 互质
hdu 5072
Solution
容斥,预处理出:
\(c_x\):表示 \(x\) 有多少个因数(雾。
\(p_x\):表示 \(x\) 的因数集合。
\(cnt_x\):表示集合中有多少个数能被 \(x\) 整除。
\[ans_x=\sum\limits_{d\in p_x}-1^{c_d}\times cnt_d
\]
点击查看代码
int n, m, k, ret, ans, a[N], cnt[N], c[N], phi[N];
vector<int> p[N];
int calc(int n) {
int ret = 0;
for(auto x : p[n]) ret += qkpow(-1, c[x]) * cnt[x];
return ret;
}
int pr[N], vis[N];
signed main() {
// freopen("1.in", "r", stdin);
const int V = 1e5;
rep(i, 2, V) {
if(!vis[i]) pr[++m] = i, c[i] = 1;
for(int j = 1; j <= m && i * pr[j] <= V; ++j) {
vis[i * pr[j]] = 1, c[i * pr[j]] = c[i] + 1;
if(i % pr[j] == 0) break;
}
}
rep(i, 1, V) for(int j = i; j <= V; j += i) p[j].pb(i);
int _; read(_);
while(_--) {
memset(cnt, 0, sizeof cnt);
read(n);
rep(i, 1, n) {
int pp = 1;
read(a[i]);
rep(j, 1, m) {
if(pr[j] * pr[j] > a[i]) break;
if(a[i] % pr[j] == 0) {
pp *= pr[j];
while(a[i] % pr[j] == 0) a[i] /= pr[j];
}
}
a[i] *= pp, ++cnt[a[i]];
}
// cerr << "fuck";
rep(i, 1, V) for(int j = i * 2; j <= V; j += i) cnt[i] += cnt[j];
rep(i, 1, n){
if(a[i] == 1) continue;
int l = calc(a[i]), r = n - l - 1;
ans += l * r;
}
write(n * (n - 1) * (n - 2) / 6 - ans / 2), putchar('\n'), ans = 0;
}
}
求 \([1,n]\) 中有多少个整数可以表示成 \(a^b(b>1)\) 的形式
hdu 2204
Solution
如果指数是合数显然会被重复统计,考虑枚举指数是质数的 case,并且已知 \(2^{60}>10^{18}\),所以只需要枚举 \([2,60]\) 之间的质数,并且这样也在一定情况下会被算重,例如 \(a^{b\times c}=a^{c\times b}\),所以要容斥,并且发现 \(2^{2\times 3 \times 5 \times 7}>10^{18}\),所以最多容斥三次即可。
理论上用 pow 会掉精度,但是对于这个题影响不大。
点击查看代码
rep(i, 1, k) {
m = (int)(pow(n, 1.0 / pr[i]));
if (m < 2)
break;
ret += m - 1;
rep(j, i + 1, k) {
m = (int)(pow(n, 1.0 / (1.0 * pr[i] * pr[j])));
if (m < 2)
break;
ret -= m - 1;
rep(l, j + 1, k) {
m = (int)(pow(n, 1.0 / (1.0 * pr[i] * pr[j] * pr[l])));
if (m < 2)
break;
ret += m - 1;
}
}
}
cout << ret + 1 << '\n';
求 \(\sum\limits_{i=a}^{b}\sum\limits_{j=c}^{d}[\gcd(i,j)=k]\)
hdu 1695(后者的弱化),P2522
Solution
很纽币的一道题,先容斥,得到(令 \([\gcd(i,j)=k]=[A]\)):
\[\sum\limits_{i=a}^b\sum\limits_{j=c}^d A=\sum\limits_{i=1}^b\sum\limits_{j=1}^d A-\sum\limits_{i=1}^{a-1}\sum\limits_{j=1}^d A-\sum\limits_{i=1}^b\sum\limits_{j=1}^{c-1} A+\sum\limits_{i=1}^{a-1}\sum\limits_{j=1}^{c-1} A
\]
故求
\[\sum\limits_{i=1}^n\sum\limits_{j=1}^m A
\]
然后用整除分块套莫比乌斯反演的结论。
点击查看代码
int calc(int n, int m, int k) {//(i, j) 和 (j, i) 视为两组
int ret = 0; n /= k, m /= k;
for(int l = 1, r; l <= min(n, m); l = r + 1) {
r = min(n / (n / l), m / (m / l));
ret += (n / l) * (m / l) * (s[r] - s[l - 1]);
}
return ret;
}
求 \(\sum\limits_{i=a}^b\sum\limits_{j=c}^d[(i+j)\equiv m\pmod p]\)
hdu 4790
Solution
跟上面差不多。
点击查看代码
int calc(int x, int y) {
int ret = 0;
for(int i = m; i <= x + y; i += p) ret += min(i - 1, x) - max(i - y, 1LL) + 1;
return ret;
}

浙公网安备 33010602011771号