日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Frogs - HDU5514

發布時間:2023/12/8 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Frogs - HDU5514 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Frogs - HDU5514

icpc沈陽2015的題目。

容斥原理和數論的應用。

題目大意為給你一個數組 a[i]a[i]a[i]a[i]a[i]a[i] 即為第 iii 只青蛙的步長) 和若干格子編號為 000m?1m - 1m?1,對于任意的正整數 k,ik, ik,i,編號為 k?a[i]modmk * a[i] \mod mk?a[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] 的倍數組成的集合為 AiA_iAi?,那么最終答案為 S(A1∪A2∪?∪An)S(A_1 \cup A_2 \cup \dots \cup A_n)S(A1?A2??An?),其中 S(A)S(A)S(A) 為集合 AAA 中所有元素的和。

這個東西顯然可以用容斥做,我們記集合 {A1,A2,…,An}\{A_1, A_2, \dots, A_n\}{A1?,A2?,,An?} 的冪集為 UUU,那么我們可以枚舉 UUU 中所有元素,寫出來容斥原理的公式:

S(?i=1nAi)=∑B∈P(?1)∣B∣?1S(?Ai∈BAi)S(\bigcup_{i = 1}^nA_i) = \sum_{B \in P}(-1)^{|B| - 1}S(\bigcap_{A_i \in B}A_i) S(i=1?n?Ai?)=BP?(?1)B?1S(Ai?B??Ai?)

(不得不說容斥原理的公式寫成這個樣子真的簡潔明了又暴力,直接枚舉了 2n2^n2n 種集合)

首先需要明確的是,我們使用容斥原理一定是要有一個出發點的,就是對于某一類集合,我們可以快速統計出來這一類集合的答案之和,因為我們不可能花費 O(2n)O(2^n)O(2n) 的時間復雜度去枚舉所有 AiA_iAi? 的交集情況,在集合數繁多的情況下我們只能對這些集合進行分類合并。

對于本題來說,這個分類方法可以很自然地得出。

我們思考若干集合的交,例如 A1∩A3∩A6A_1 \cap A_3 \cap A_6A1?A3?A6?,那么在這個集合中的元素一定是 a[1],a[3],a[6]a[1], a[3], a[6]a[1],a[3],a[6] 的公倍數,即一定是 lcm(a[1],a[3],a[6])lcm(a[1], a[3], a[6])lcm(a[1],a[3],a[6]) 的倍數,并且 000mmm 內所有 a[1],a[3],a[6]a[1], a[3], a[6]a[1],a[3],a[6] 的公倍數一定在這個集合中。

對于任意一組集合的并皆是如此,并且,我們不難發現,由于 a[i]a[i]a[i] 都是 mmm 的因數,那么若干個 a[i]a[i]a[i] 的最小公倍數也一定是 mmm 的因數。

所以,我們不妨把集合按照最小公倍數分類來進行容斥。

首先,根據上面的結論,我們有:

S(?Ai∈BAi)=12lml(ml?1)=12m(ml?1)S(\bigcap_{A_i \in B} A_i) = \frac 12 l\frac ml(\frac ml - 1) = \frac 12m(\frac ml - 1) S(Ai?B??Ai?)=21?llm?(lm??1)=21?m(lm??1)

其中 lll 為所有 Ai∈BA_i \in BAi?Biiia[i]a[i]a[i] 的最小公倍數。

我們不妨設

l=LCM(B)=lcmAi∈Ba[i]l = LCM(B) = {lcm}_{A_i \in B} a[i] l=LCM(B)=lcmAi?B?a[i]

那么我們就可以以對集合按照最小公倍數分類的方式重寫容斥原理。

S(?i=1nAi)=∑l∣m∑B∈P[LCM(B)=l](?1)∣B∣?1S(?Ai∈BAi)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=1?n?Ai?)=lm?BP?[LCM(B)=l](?1)B?1S(Ai?B??Ai?)

=∑l∣m∑B∈P[LCM(B)=l](?1)∣B∣?112m(ml?1)= \sum_{l|m}\sum_{B \in P}[LCM(B) = l](-1)^{|B| - 1}\frac 12m(\frac ml - 1) =lm?BP?[LCM(B)=l](?1)B?121?m(lm??1)

=12m∑l∣m(ml?1)∑B∈P[LCM(B)=l](?1)∣B∣?1= \frac 12m\sum_{l|m}(\frac ml - 1)\sum_{B \in P}[LCM(B) = l](-1)^{|B| - 1} =21?mlm?(lm??1)BP?[LCM(B)=l](?1)B?1

接下來,我們只需要考慮如何計算

∑B∈P[LCM(B)=l](?1)∣B∣?1\sum_{B \in P}[LCM(B) = l](-1)^{|B| - 1} BP?[LCM(B)=l](?1)B?1

這一坨式子。

這里我們需要用到一個結論,也就是非常出名的莫比烏斯反演公式:

如果有函數 f(n),g(n)f(n), g(n)f(n),g(n) 滿足:

g(n)=∑d∣nf(d)g(n) = \sum_{d|n}f(d) g(n)=dn?f(d)

那么他們同時也滿足:

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

這是一個非常非常非常有用的結論,他可以允許我們在枚舉最大公約數或最小公倍數時去掉最大/最小,改為枚舉公約數或者公倍數。這里的 μ\muμ 是莫比烏斯函數,關于莫比烏斯反演在我另一篇博客中有講解,可以移步去看,不過這里我們只需要知道這個結論就行了。

在這里,我們不妨設

f(l)=∑B∈P[LCM(B)=l](?1)∣B∣?1f(l) = \sum_{B \in P}[LCM(B) = l](-1)^{|B| - 1} f(l)=BP?[LCM(B)=l](?1)B?1

那么我們不難發現對于

g(n)=∑d∣nf(d)g(n) = \sum_{d|n}f(d) g(n)=dn?f(d)

來說,f(n)f(n)f(n) 表示所有最小公倍數為 nnn 的集合 BBB(?1)∣B∣?1(-1)^{|B| - 1}(?1)B?1 的和,那么 g(n)g(n)g(n) 即表示所有公倍數為 nnn 的集合 BBB(?1)∣B∣?1(-1)^{|B| - 1}(?1)B?1 的和。

我們通過求出來 g(n)g(n)g(n) 的式子,然后利用莫比烏斯反演的公式來間接求出 f(n)f(n)f(n) 的式子。

g(n)g(n)g(n) 的式子非常好求,我們只需要知道數組 a[i]a[i]a[i] 中為 nnn 的約數的數的個數即可,假設個數為 xxx

那么這些約數中任意若干個約數組成的集合都滿足公約數為 nnn,即

g(n)=∑i=1x(xi)(?1)i?1=1?(1?1)xg(n) = \sum_{i = 1}^x{x \choose i}(-1)^{i - 1} = 1 - (1 - 1)^x g(n)=i=1x?(ix?)(?1)i?1=1?(1?1)x

我們可以得到,當數組 a[i]a[i]a[i] 中為 nnn 的約數的個數為 000 時,g(n)=0g(n) = 0g(n)=0,否則 g(n)=1g(n) = 1g(n)=1。

我們把這個式子代入原式:

S(?i=1nAi)=12m∑l∣m(ml?1)f(l)S(\bigcup_{i = 1}^nA_i) = \frac 12m\sum_{l|m}(\frac ml - 1)f(l) S(i=1?n?Ai?)=21?mlm?(lm??1)f(l)

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

我們不妨交換一下求和順序,將 g(d)g(d)g(d) 提前,得到

S(?i=1nAi)=12m∑d∣mg(d)∑l∣mdμ(l)(mdl?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=1?n?Ai?)=21?mdm?g(d)ldm??μ(l)(dlm??1)

我們設

T(n)=∑d∣nμ(d)(nd?1)=∑d∣nμ(d)nd?∑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)(dn??1)=dn?μ(d)dn??dn?μ(d)=?(n)?e(n)

這里的 ?(n)\phi(n)?(n) 是歐拉函數,e(n)e(n)e(n) 是單位函數,僅當 n=1n = 1n=1e(n)=1e(n) = 1e(n)=1,否則 e(n)=0e(n) = 0e(n)=0。

如果你不知道什么是歐拉函數或者單位函數,那你就把它當做一個普通積性函數就可以了,我們仍可以通過積性函數的性質用篩法篩出來函數的值。

所以,最終答案為:

S(?i=1nAi)=12m∑d∣mg(d)T(nd)S(\bigcup_{i = 1}^nA_i) = \frac 12m\sum_{d|m}g(d)T(\frac nd) S(i=1?n?Ai?)=21?mdm?g(d)T(dn?)

=12m∑d∣mg(d)[?(nd)?e(nd)]= \frac 12m\sum_{d|m}g(d)[\phi(\frac nd) - e(\frac nd)] =21?mdm?g(d)[?(dn?)?e(dn?)]

這樣,我們就可以先求出來 mmm 的所有約數,然后對于每個約數 ddd,求出來 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)mmm 的因數個數,這個值很小,最大只有不到 400040004000 個(據說)。

然而最終代碼還是跑得很快的,甚至只需要 71ms71ms71ms。

#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; }

總結

以上是生活随笔為你收集整理的Frogs - HDU5514的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。