您的位置:首页 > Web前端

[BZOJ1005][HNOI2008]明明的烦恼(prufer计数+组合数)

2017-12-03 21:21 519 查看
prufer序列链接:https://www.cnblogs.com/dirge/p/5503289.html

可以发现,这题是一个无根树计数的模型,可以利用prufer序列求解。如果不考虑每一个点的度数限制,那么结果就是nn−2,而根据prufer序列的构造方法能够得到,对于任意一个点i,i在prufer序列中的出现次数,等于点i的度数减1。

这时候,就变成了在n−2个值中选D1−1个值,定为1,再在剩下的n−1−D1中选D2−1个值,定为2,然后在剩下的n−D1−D2中选出D3−1个值定为3,……,最后还没确定的值,可以定为1到<
ba1b
span style="display: inline-block; position: relative; width: 0.589em; height: 0px; font-size: 125%;">n中的任意数值。

这本来可以用组合数简单解决,然而这题偏偏是高精……

高精求组合数,可以用质因数分解的方法计算。

代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 1005, M = 905, ZZQ = 1e6;
int n, m, sm, dm, d
, cnt
, pn, pri
, pw

, sum

;
bool vis
;
void calc(int n, int sgn) {
int i; for (i = 1; i <= pn; i++)
cnt[i] += sgn * sum
[i];
}
struct cyx {
int n; ll a
;
cyx() {}
cyx(int _n) :
n(_n) {memset(a, 0, sizeof(a));}
};
cyx num(int a) {
cyx res = cyx(1); res.a[1] = a;
return res;
}
cyx prod(cyx a, cyx b) {
int i, j; cyx res = cyx(a.n + b.n + 2);
for (i = 1; i <= a.n; i++) for (j = 1; j <= b.n; j++)
res.a[i + j - 1] += a.a[i] * b.a[j];
for (i = 1; i <= res.n; i++) res.a[i + 1] += res.a[i] / ZZQ,
res.a[i] %= ZZQ; while (res.n > 1 && !res.a[res.n]) res.n--;
return res;
}
cyx qpow(cyx a, int b) {
cyx res = num(1);
while (b) {
if (b & 1) res = prod(res, a);
a = prod(a, a);
b >>= 1;
}
return res;
}
void write(cyx a) {
int i; for (i = a.n; i; i--)
if (i == a.n) printf("%d", a.a[i]);
else printf("%06d", a.a[i]);
printf("\n");
}
int main() {
int i, j; n = read(); for (i = 1; i <= n; i++) d[i] = read();
for (i = 2; i * i <= 1000; i++) {
if (vis[i]) continue;
for (j = i * i; j <= 1000; j += i)
vis[j] = 1;
}
for (i = 2; i <= 1000; i++) if (!vis[i]) pri[++pn] = i;
for (i = 1; i <= 1000; i++) for (j = 1; j <= pn; j++) {
int x = i; while (x % pri[j] == 0) pw[i][j]++, x /= pri[j];
sum[i][j] = sum[i - 1][j] + pw[i][j];
}
if (n == 1) return printf("%d\n", (d[1] == 0 || d[1] == -1)), 0;
if (n == 2) return printf("%d\n", ((d[1] == 1 || d[1] == -1)
&& (d[2] == 1 || d[2] == -1))), 0;
for (i = 1; i <= n; i++) if (d[i] < -1 || d[i] == 0) return puts("0"), 0;
for (i = 1; i <= n; i++) if (d[i] != -1) sm += (--d[i]); else m++;
if (sm > n - 2 || (!m && sm != n - 2)) return puts("0"), 0;
dm = n - 2; sm = dm - sm; calc(dm, 1);
for (i = 1; i <= n; i++) if (d[i] != -1) calc(d[i], -1), dm -= d[i];
calc(dm, -1); cyx ans = num(1);
for (i = 1; i <= pn; i++) ans = prod(ans, qpow(num(pri[i]), cnt[i]));
ans = prod(ans, qpow(num(m), sm)); write(ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: