一些 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;
}
posted @ 2025-04-28 16:32  Iron_Spade  阅读(13)  评论(0)    收藏  举报