標籤:style blog http color get width
題目連結:uva 1436 - Counting heaps
題目大意:給出一個樹的形狀,現在為這棵樹標號,保證根節點的標號值比子節點的標號值大,問有多少種標號樹。
解題思路:和村名排隊的思路是一隻的uva11174,最後問題只和樹德結構有直接關係,f(root)=(s(root)?1)!(s(1)?s(2)???s(n)
但是給定的模數數不是質數,所以不能用逆元做,只能將分子分母分別拆分成質因子,然後對質因子進位約分。因為最後的答案一定是正整數,所以對於每個質因子,分子分解出的因子個數一定大於等於分母分解出的,最後約分肯定剩下的是分子,再用快速冪求解。
剪枝,因為分解質因子的次數非常多,所以需要對分解函數剪枝,當u是質數時,可以直接終止返回。
#include <cstdio>#include <cstring>#include <queue>using namespace std;const int N = 500005;typedef long long ll;int P = 0, prime[N], ispri[N];int n, f[N], vis[N], cnt[N];ll mod;void getPrime (int N) { memset(ispri, 0, sizeof(ispri)); for (int i = 2; i < N; i++) { if (ispri[i]) continue; prime[P++] = i; for (int j = 2 * i; j < N; j += i) ispri[j] = 1; }}void getNode () { memset(cnt, 0, sizeof(cnt)); queue<int> que; for (int i = 1; i <= n; i++) if (vis[i] == 0) que.push(i); while (!que.empty()) { int u = que.front(); que.pop(); cnt[u]++; int v = f[u]; cnt[v] += cnt[u]; vis[v]--; if (vis[v] == 0) que.push(v); }}void init () { memset(vis, 0, sizeof(vis)); scanf("%d%lld", &n, &mod); f[1] = 0; for (int i = 2; i <= n; i++) { scanf("%d", &f[i]); vis[f[i]]++; } getNode(); memset(vis, 0, sizeof(vis)); for (int i = 1; i <= n; i++) vis[cnt[i]]++;}ll power (ll x, ll m) { ll ans = 1; while (m) { if (m&1) ans = ans * x % mod; x = x * x % mod; m /= 2; } return ans;}void cal (int u, int v) { for (int i = 0; i < P; i++) { int k = prime[i]; while (u % k == 0) { cnt[k] += v; u /= k; } if (ispri[u] == 0) { cnt[u] += v; return; } }}ll solve () { memset(cnt, 0, sizeof(cnt)); for (int i = 2; i <= n; i++) cal(i, 1); for (int i = 2; i <= n; i++) if (vis[i]) cal(i, -vis[i]); ll ans = 1; for (int i = 0; i < P; i++) { ll u = prime[i]; if (cnt[u]) ans = (ans * power(u, cnt[u])) % mod; } return ans;}int main () { getPrime(N); int cas; scanf("%d", &cas); while (cas--) { init(); printf("%lld\n", solve()); } return 0;}