Frogs - HDU5514

Frogs - HDU5514

icpc沈阳2015的题目。

容斥原理和数论的应用。

题目大意为给你一个数组 a [ i ] a[i] a[i] a [ i ] a[i] a[i] 即为第 i i i 只青蛙的步长) 和若干格子编号为 0 0 0 m − 1 m - 1 m1,对于任意的正整数 k , i k, i k,i,编号为 k ∗ a [ i ] m o d    m k * a[i] \mod m ka[i]modm 的格子会被占用,问所有被占用的格子的编号之和。


首先对于一个 a [ i ] a[i] a[i],显然所有编号为 gcd ⁡ ( a [ i ] , m ) \gcd(a[i], m) gcd(a[i],m) 的倍数的格子会被占用,抛弃掉之前的 a [ i ] a[i] a[i] ,直接将 gcd ⁡ ( a [ i ] , m ) \gcd(a[i], m) gcd(a[i],m) 设为新的 a [ i ] a[i] a[i],这样的话,我们记 a [ i ] a[i] a[i] 的倍数组成的集合为 A i A_i Ai,那么最终答案为 S ( A 1 ∪ A 2 ∪ ⋯ ∪ A n ) S(A_1 \cup A_2 \cup \dots \cup A_n) S(A1A2An),其中 S ( A ) S(A) S(A) 为集合 A A A 中所有元素的和。

这个东西显然可以用容斥做,我们记集合 { A 1 , A 2 , … , A n } \{A_1, A_2, \dots, A_n\} {A1,A2,,An} 的幂集为 U U U,那么我们可以枚举 U U U 中所有元素,写出来容斥原理的公式:

S ( ⋃ i = 1 n A i ) = ∑ B ∈ P ( − 1 ) ∣ B ∣ − 1 S ( ⋂ A i ∈ B A i ) S(\bigcup_{i = 1}^nA_i) = \sum_{B \in P}(-1)^{|B| - 1}S(\bigcap_{A_i \in B}A_i) S(i=1nAi)=BP(1)B1S(AiBAi)

(不得不说容斥原理的公式写成这个样子真的简洁明了又暴力,直接枚举了 2 n 2^n 2n 种集合)

首先需要明确的是,我们使用容斥原理一定是要有一个出发点的,就是对于某一类集合,我们可以快速统计出来这一类集合的答案之和,因为我们不可能花费 O ( 2 n ) O(2^n) O(2n) 的时间复杂度去枚举所有 A i A_i Ai 的交集情况,在集合数繁多的情况下我们只能对这些集合进行分类合并。

对于本题来说,这个分类方法可以很自然地得出。

我们思考若干集合的交,例如 A 1 ∩ A 3 ∩ A 6 A_1 \cap A_3 \cap A_6 A1A3A6,那么在这个集合中的元素一定是 a [ 1 ] , a [ 3 ] , a [ 6 ] a[1], a[3], a[6] a[1],a[3],a[6] 的公倍数,即一定是 l c m ( a [ 1 ] , a [ 3 ] , a [ 6 ] ) lcm(a[1], a[3], a[6]) lcm(a[1],a[3],a[6]) 的倍数,并且 0 0 0 m m m 内所有 a [ 1 ] , a [ 3 ] , a [ 6 ] a[1], a[3], a[6] a[1],a[3],a[6] 的公倍数一定在这个集合中。

对于任意一组集合的并皆是如此,并且,我们不难发现,由于 a [ i ] a[i] a[i] 都是 m m m 的因数,那么若干个 a [ i ] a[i] a[i] 的最小公倍数也一定是 m m m 的因数。

所以,我们不妨把集合按照最小公倍数分类来进行容斥。

首先,根据上面的结论,我们有:

S ( ⋂ A i ∈ B A i ) = 1 2 l m l ( m l − 1 ) = 1 2 m ( m l − 1 ) S(\bigcap_{A_i \in B} A_i) = \frac 12 l\frac ml(\frac ml - 1) = \frac 12m(\frac ml - 1) S(AiBAi)=21llm(lm1)=21m(lm1)

其中 l l l 为所有 A i ∈ B A_i \in B AiB i i i a [ i ] a[i] a[i] 的最小公倍数。

我们不妨设

l = L C M ( B ) = l c m A i ∈ B a [ i ] l = LCM(B) = {lcm}_{A_i \in B} a[i] l=LCM(B)=lcmAiBa[i]

那么我们就可以以对集合按照最小公倍数分类的方式重写容斥原理。

S ( ⋃ i = 1 n A i ) = ∑ l ∣ m ∑ B ∈ P [ L C M ( B ) = l ] ( − 1 ) ∣ B ∣ − 1 S ( ⋂ A i ∈ B A i ) S(\bigcup_{i = 1}^nA_i) = \sum_{l|m}\sum_{B \in P}[LCM(B) = l](-1)^{|B| - 1}S(\bigcap_{A_i \in B}A_i) S(i=1nAi)=lmBP[LCM(B)=l](1)B1S(AiBAi)

= ∑ l ∣ m ∑ B ∈ P [ L C M ( B ) = l ] ( − 1 ) ∣ B ∣ − 1 1 2 m ( m l − 1 ) = \sum_{l|m}\sum_{B \in P}[LCM(B) = l](-1)^{|B| - 1}\frac 12m(\frac ml - 1) =lmBP[LCM(B)=l](1)B121m(lm1)

= 1 2 m ∑ l ∣ m ( m l − 1 ) ∑ B ∈ P [ L C M ( B ) = l ] ( − 1 ) ∣ B ∣ − 1 = \frac 12m\sum_{l|m}(\frac ml - 1)\sum_{B \in P}[LCM(B) = l](-1)^{|B| - 1} =21mlm(lm1)BP[LCM(B)=l](1)B1

接下来,我们只需要考虑如何计算

∑ B ∈ P [ L C M ( B ) = l ] ( − 1 ) ∣ B ∣ − 1 \sum_{B \in P}[LCM(B) = l](-1)^{|B| - 1} BP[LCM(B)=l](1)B1

这一坨式子。

这里我们需要用到一个结论,也就是非常出名的莫比乌斯反演公式:

如果有函数 f ( n ) , g ( n ) f(n), g(n) f(n),g(n) 满足:

g ( n ) = ∑ d ∣ n f ( d ) g(n) = \sum_{d|n}f(d) g(n)=dnf(d)

那么他们同时也满足:

f ( n ) = ∑ d ∣ n μ ( n d ) g ( d ) f(n) = \sum_{d|n}\mu(\frac nd)g(d) f(n)=dnμ(dn)g(d)

这是一个非常非常非常有用的结论,他可以允许我们在枚举最大公约数或最小公倍数时去掉最大/最小,改为枚举公约数或者公倍数。这里的 μ \mu μ 是莫比乌斯函数,关于莫比乌斯反演在我另一篇博客中有讲解,可以移步去看,不过这里我们只需要知道这个结论就行了。

在这里,我们不妨设

f ( l ) = ∑ B ∈ P [ L C M ( B ) = l ] ( − 1 ) ∣ B ∣ − 1 f(l) = \sum_{B \in P}[LCM(B) = l](-1)^{|B| - 1} f(l)=BP[LCM(B)=l](1)B1

那么我们不难发现对于

g ( n ) = ∑ d ∣ n f ( d ) g(n) = \sum_{d|n}f(d) g(n)=dnf(d)

来说, f ( n ) f(n) f(n) 表示所有最小公倍数为 n n n 的集合 B B B ( − 1 ) ∣ B ∣ − 1 (-1)^{|B| - 1} (1)B1 的和,那么 g ( n ) g(n) g(n) 即表示所有公倍数为 n n n 的集合 B B B ( − 1 ) ∣ B ∣ − 1 (-1)^{|B| - 1} (1)B1 的和。

我们通过求出来 g ( n ) g(n) g(n) 的式子,然后利用莫比乌斯反演的公式来间接求出 f ( n ) f(n) f(n) 的式子。

g ( n ) g(n) g(n) 的式子非常好求,我们只需要知道数组 a [ i ] a[i] a[i] 中为 n n n 的约数的数的个数即可,假设个数为 x x x

那么这些约数中任意若干个约数组成的集合都满足公约数为 n n n,即

g ( n ) = ∑ i = 1 x ( x i ) ( − 1 ) i − 1 = 1 − ( 1 − 1 ) x g(n) = \sum_{i = 1}^x{x \choose i}(-1)^{i - 1} = 1 - (1 - 1)^x g(n)=i=1x(ix)(1)i1=1(11)x

我们可以得到,当数组 a [ i ] a[i] a[i] 中为 n n n 的约数的个数为 0 0 0 时, g ( n ) = 0 g(n) = 0 g(n)=0,否则 g ( n ) = 1 g(n) = 1 g(n)=1

我们把这个式子代入原式:

S ( ⋃ i = 1 n A i ) = 1 2 m ∑ l ∣ m ( m l − 1 ) f ( l ) S(\bigcup_{i = 1}^nA_i) = \frac 12m\sum_{l|m}(\frac ml - 1)f(l) S(i=1nAi)=21mlm(lm1)f(l)

= 1 2 m ∑ l ∣ m ( m l − 1 ) ∑ d ∣ l μ ( l d ) g ( d ) = \frac 12m\sum_{l|m}(\frac ml - 1)\sum_{d|l}\mu(\frac ld)g(d) =21mlm(lm1)dlμ(dl)g(d)

我们不妨交换一下求和顺序,将 g ( d ) g(d) g(d) 提前,得到

S ( ⋃ i = 1 n A i ) = 1 2 m ∑ d ∣ m g ( d ) ∑ l ∣ m d μ ( l ) ( m d l − 1 ) S(\bigcup_{i = 1}^nA_i) = \frac 12m\sum_{d|m}g(d)\sum_{l|\frac md}\mu(l)(\frac m{dl} - 1) S(i=1nAi)=21mdmg(d)ldmμ(l)(dlm1)

我们设

T ( n ) = ∑ d ∣ n μ ( d ) ( n d − 1 ) = ∑ d ∣ n μ ( d ) n d − ∑ d ∣ n μ ( d ) = ϕ ( n ) − e ( n ) T(n) = \sum_{d|n}\mu(d)(\frac nd - 1) = \sum_{d|n}\mu(d)\frac nd - \sum_{d|n}\mu(d) = \phi(n) - e(n) T(n)=dnμ(d)(dn1)=dnμ(d)dndnμ(d)=ϕ(n)e(n)

这里的 ϕ ( n ) \phi(n) ϕ(n) 是欧拉函数, e ( n ) e(n) e(n) 是单位函数,仅当 n = 1 n = 1 n=1 e ( n ) = 1 e(n) = 1 e(n)=1,否则 e ( n ) = 0 e(n) = 0 e(n)=0

如果你不知道什么是欧拉函数或者单位函数,那你就把它当做一个普通积性函数就可以了,我们仍可以通过积性函数的性质用筛法筛出来函数的值。

所以,最终答案为:

S ( ⋃ i = 1 n A i ) = 1 2 m ∑ d ∣ m g ( d ) T ( n d ) S(\bigcup_{i = 1}^nA_i) = \frac 12m\sum_{d|m}g(d)T(\frac nd) S(i=1nAi)=21mdmg(d)T(dn)

= 1 2 m ∑ d ∣ m g ( d ) [ ϕ ( n d ) − e ( n d ) ] = \frac 12m\sum_{d|m}g(d)[\phi(\frac nd) - e(\frac nd)] =21mdmg(d)[ϕ(dn)e(dn)]

这样,我们就可以先求出来 m m m 的所有约数,然后对于每个约数 d d d,求出来 g ( d ) , ϕ ( d ) g(d), \phi(d) g(d),ϕ(d) 的值。

时间复杂度为 O ( d ( m ) n ) O(d(m)n) O(d(m)n),其中 d ( m ) d(m) d(m) m m m 的因数个数,这个值很小,最大只有不到 4000 4000 4000 个(据说)。

然而最终代码还是跑得很快的,甚至只需要 71 m s 71ms 71ms

#include <bits/stdc++.h>

const int MAXN = 1e5 + 5;

int a[MAXN], ddiv[MAXN], g[MAXN], phi[MAXN], n, m, cnt, prime[MAXN], cnt2;

int find(int x) {
	return std::lower_bound(ddiv + 1, ddiv + cnt + 1, x) - ddiv;
}

int e(int x) {
	return x == 1;
}

void solve(int T) {
	scanf("%d%d", &n, &m);
	cnt = cnt2 = 0;
	/*memset(g, 0, sizeof(g));
	memset(phi, 0, sizeof(phi));
	memset(ddiv, 0, sizeof(ddiv));
	memset(prime, 0, sizeof(prime));*/
	for(int i = 1; i <= n; i++) {
		int x;
		scanf("%d", &x);
		a[i] = std::__gcd(x, m);
	}
	std::sort(a + 1, a + n + 1);
	n = std::unique(a + 1, a + n + 1) - a - 1;
	for(int i = 1; i * i <= m; i++) {
		if(m % i != 0) continue;
		int d1 = i, d2 = m / i;
		ddiv[++cnt] = d1;
		if(d1 != d2) ddiv[++cnt] = d2;
	}
	std::sort(ddiv + 1, ddiv + cnt + 1);
	phi[1] = 1;
	for(int i = 2; i <= cnt; i++) {
		phi[i] = ddiv[i] - 1;
		for(int j = 1; j <= cnt2; j++) {
			if(ddiv[i] % prime[j] != 0) continue;
			int t1 = ddiv[i] / prime[j];
			if(t1 % prime[j] == 0) phi[i] = phi[find(t1)] * prime[j];
			else phi[i] = phi[find(t1)] * (prime[j] - 1);
		}
		if(phi[i] == ddiv[i] - 1) prime[++cnt2] = ddiv[i];
	}
	for(int i = 1; i <= cnt; i++) {
		g[i] = 0;
		for(int j = 1; j <= n; j++)
			if(ddiv[i] % a[j] == 0) {g[i] = 1; break;}
	}
	long long ans = 0;
	for(int i = 1; i <= cnt; i++) ans = ans + 1LL * g[i] * (phi[find(m / ddiv[i])] - (i == cnt));
	ans = ans * m / 2;
	printf("Case #%d: %lld\n", T, ans);
}

int main() {
	int T;
	scanf("%d", &T);
	for(int i = 1; i <= T; i++) solve(i);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
“Poison frogs! targeted clean-label poisoning attacks on neural networks”这是一个关于对神经网络进行有针对性的干净标签中毒攻击的研究项目。在这种攻击中,研究人员通过修改训练数据集中的特定标签,以欺骗神经网络模型以误分类输入样本。 干净标签中毒攻击是一种隐蔽的攻击方式,因为攻击者不需要修改图像本身或添加任何可见的攻击标记。相反,他们通过对训练数据集进行精心设计的修改,使神经网络在应用中出现错误分类。这种攻击方法可能会导致严重后果,例如在自动驾驶汽车或安全系统中造成事故或功能失效。 这个项目的目的是研究这种攻击方法的可行性和效果,并提出对抗这种攻击的解决方案。研究人员首先对训练数据集进行修改,以使特定类别的图像被误分类为其他类别。然后,他们使用已经训练好的神经网络模型,通过修改训练数据集中的特定图像标签,使模型在测试阶段错误地将这些特定图像分类为不同的类别。 结果表明,即使在高性能的神经网络上进行干净标签中毒攻击也是可行的。这些攻击可以在不影响模型在其他正常输入上性能的情况下,误导模型对特定图像的分类。这使得攻击者能够操纵模型的行为,甚至可能导致系统的安全漏洞和错误决策。 为了对抗这种攻击,研究人员提出了一种改进的训练策略,称为“防御机制”。这种方法可以增强模型对干净标签中毒攻击的鲁棒性,并提供了一种有效的方法来检测和解决这种攻击。 总体而言,这个项目揭示了干净标签中毒攻击在神经网络中的潜在威胁,并提供了对抗这种攻击的解决方案。这有助于进一步加强神经网络模型在面临安全挑战时的鲁棒性,并推动相关领域的研究和技术发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值