【問題描述】
話說正在jmy愁苦如何籌錢給大家買汽水的時候,他遇上了一位魔法師。魔法師希望jmy能幫他破解魔法書的咒語。如果jmy做到了,就幫他付所有買汽水的錢。
魔法書上畫了乙個完全圖(每兩個點之間有且只有一條邊),每個點都有乙個獨一無二的[1,n]內的編號,jmy的任務是要找到最小生成樹,以此作為魔法樹,從而破解咒語。
對於完全圖的邊(i,j)(i≠j)的邊權恰好就等於i,j 兩個數字的最大公約數。
特別地,要作為魔法樹,必須滿足樹指定某個點為根後,所有除根以外的節點的父親的標號必須小於自身標號。
jmy一眼就看出了最小生成樹的邊權和。然而咒語卻是最小生成樹的個數。
為了保證大家都有汽水喝,你能幫幫jmy嗎?
【輸入格式】
一行僅乙個數n表示完全圖的大小。
【輸出格式】
一行乙個整數表示答案對100,000,007取mod的結果。
【輸入輸出樣例】
mst.in
3 mst.out
2【資料規模】
對於10%的資料n≤5
對於30%的資料n≤8
對於40%的資料n≤10
對於70%的資料n≤5,000
對於100%的資料n≤20,000
【分析】
首先肯定存在一種最小生成樹方案:令點1為根,向其餘每個點連邊。也就是說,所有的最小生成樹方案都滿足所有的邊權為1。又因為「所有除根以外的節點的父親的標號必須小於自身標號」,所以我們考慮按照編號順序構造最小生成樹。那麼如果當前的生成樹中已經包含點
1 ~i−
1,則點
i 可選擇向任意乙個與
i互質的點連邊。
我們記f[i
] 表示已經最小生成樹包含點
i 的方案個數,則轉移為 f[
i]=f
[i−1
]×(點1
~i−1
中與點i
互質的點的個數)
我們記cnt
[i]表示互質點的個數,問題的關鍵在於如何求出cn
t[i]
。 一開始先假設cn
t[i]
=i−1
,即前面的所有數都與它互質,然後扣除掉那些不合法的方案。記
i 的倍數k=
i×j(
j≥2)
,則cnt
[k]−
=cnt
[i] ,表示所有與
i 互質的數乘以
j後必然與i×
j 不互質,所以要扣除。因為這樣是從互質的個數來轉移,
j 為i×
j和與i 互質的數乘以
j後的最大公約數,所以不會算重。
【**】
#include
#include
using
namespace
std;
typedef
long
long ll;
const ll mod = 1e8 + 7;
const
int n = 2e4 + 5;
ll f[n], cnt[n]; bool vis[n];
int n, m, a[n];
int main()
最小生成樹訓練總結
最小生成樹簡單概念 在數個點間,兩點之間存在權值,求若涉及所有的點時,權值之和最小。常用演算法 1.prim演算法 首先將每乙個點之間的距離都定為最大值,把第乙個點的最小距離設為零,在與第乙個點有聯絡的每乙個點中找出權值最小的點,依此規律進行迴圈,直到找到所有的點。例 最優佈線問題中的迴圈部分 fo...
最小生成樹 次小生成樹
一 最小生成樹 說到生成樹首先要解釋一下樹,樹是乙個聯通的無向無環圖,多棵樹的集合則被稱為森林。因此,樹具有許多性質 1.兩點之間的路徑是唯一的。2.邊數等於點數減一。3.連線任意兩點都會生成乙個環。對於乙個無向聯通圖g的子圖,如果它包含g的所有點,則它被稱為g的生成樹,而各邊權和最小的生成樹則被稱...
最小生成樹
package 圖 最小生成樹是用最少的邊吧把所有的節點連線起來。於是和圖的深度優先搜素差不多。class stack public void push int key public int pop 檢視棧頂的元素 public int peek public boolean isempty cla...