您的位置:首页 > 其它

JZOJ 5180 呵呵

2018-03-22 18:26 204 查看
传送门

考场上的思路

正解

参考代码

总结

传送门

考场上的思路

  想到了 Prüfer 序列,然而却没法解决权值的问题。想的是用 Prüfer 序列还原整棵树,却发现时间复杂度不对,所以就没有写。

正解

  考虑某一个度序列对答案的贡献:(aa 代表度数)

∑a1+a2+⋯+an=2n−2(n−2)!(a1−1)!⋯(an−1)!⋅wa11⋯wann⋅a1⋯an∑a1+a2+⋯+an=2n−2(n−2)!(a1−1)!⋯(an−1)!⋅w1a1⋯wnan⋅a1⋯an

  左边代表给定个数 Prüfer 序列对应的子树个数。中间代表考虑了重边,因为两点间的边数为 wiwjwiwj,而对应的个数为各 wiwjwiwj 的乘积,把它们按点分类即可得到 waiiwiai。最后代表了对答案的贡献。

(为什么会想到度序列,因为题目中要求的东西跟度数有紧密联系,考试的时候我就是忘了)

  −1−1 是不好看的,所以我们让所有 aa 都减一,把所有与 aa 无关的项提出来:

w1⋯wn⋅(n−2)!∑a1+a2+⋯+an=n−21a1!⋯an!⋅wa11⋯wann⋅(a1+1)⋯(an+1)w1⋯wn⋅(n−2)!∑a1+a2+⋯+an=n−21a1!⋯an!⋅w1a1⋯wnan⋅(a1+1)⋯(an+1)

  现在相当于是要求右边与 aa 有关的东西:

∑a1+a2+⋯+an=n−2wa11⋯wanna1!⋯an!⋅(a1+1)⋯(an+1)∑a1+a2+⋯+an=n−2w1a1⋯wnana1!⋯an!⋅(a1+1)⋯(an+1)

  考虑把 (ai+1)(ai+1) 拆开,相当于就有 nn 个二项式相乘,对于某一项,它的组合意义是选则 aiai 还是 11,于是我们枚举所有可能(共有 2n2n 个项需要枚举,这里用 {ap}{ap} 表示枚举出的集合):

∑a1+a2+⋯+an=n−2∑{ap}ap1ap2⋯apk⋅wa11⋯wanna1!⋯an!∑a1+a2+⋯+an=n−2∑{ap}ap1ap2⋯apk⋅w1a1⋯wnana1!⋯an!

  提出内层到中层:

∑a1+a2+⋯+an=n−2wa11⋯wanna1!⋯an!⋅∑{ap}ap1ap2⋯apk∑a1+a2+⋯+an=n−2w1a1⋯wnana1!⋯an!⋅∑{ap}ap1ap2⋯apk

  约分可得:

∑a1+a2+⋯+an=n−2wa11⋯wanna1!⋯(ap1−1)!(ap2−1)!⋯(apk−1)!⋯an!⋅∑{ap}1⋅1⋯1∑a1+a2+⋯+an=n−2w1a1⋯wnana1!⋯(ap1−1)!(ap2−1)!⋯(apk−1)!⋯an!⋅∑{ap}1⋅1⋯1

  令 {ap}{ap} 中的所有元素减一(设 |{ap}|=k|{ap}|=k,因此最后我们可能要枚举 kk),则上式等于:

∑a1+a2+⋯+an=n−2−k∑{ap}wa11⋯wap1+1p1wap2+1p2⋯wapk+1pk⋯wanna1!⋯ap1!ap2!⋯apk!⋯an!⋅∑a1+a2+⋯+an=n−2−k∑{ap}w1a1⋯wp1ap1+1wp2ap2+1⋯wpkapk+1⋯wnana1!⋯ap1!ap2!⋯apk!⋯an!⋅

  把多的 wpiwpi 提出来:

∑a1+a2+⋯+an=n−2−k∑{ap}∏ni=1waii∏ni=1ai!⋅wp1wp2⋯wpk∑a1+a2+⋯+an=n−2−k∑{ap}∏i=1nwiai∏i=1nai!⋅wp1wp2⋯wpk

  把多的 wpiwpi 再往外提:

⎛⎝∑{ap}wp1wp2⋯wpk⎞⎠×(∑a1+a2+⋯+an=n−2−k∏ni=1waii∏ni=1ai!)(∑{ap}wp1wp2⋯wpk)×(∑a1+a2+⋯+an=n−2−k∏i=1nwiai∏i=1nai!)

  前面部分我们用一个 DP 去做,后面部分的下面是一个数的阶乘,那些数的和是 n−2−kn−2−k,上面是一些数的乘积。考虑指数型生成函数,可以看作是这些生成函数的乘积(这一步怎么来的?你手算一下,乘一下看看对不对,就知道了):

A^i(x)=w0i+w1i1!x+w2i2!x2+⋯+wnin!xnA^i(x)=wi0+wi11!x+wi22!x2+⋯+winn!xn

  根据生成函数的那套理论,泰勒展开 exex 有:

ex=1+x1!+x22!+x33!+⋯ex=1+x1!+x22!+x33!+⋯

  所以有:

A^i(x)=ewi⋅xA^i(x)=ewi⋅x

  所以那些生成函数的乘积为:

∏i=1nA^i=ex∑w∏i=1nA^i=ex∑w

  把它泰勒展开(不会?上面不是给了式子吗):

∏i=1nA^i=1+(∑w)1!x+(∑w)22!x2+⋯+(∑w)nn!xn∏i=1nA^i=1+(∑w)1!x+(∑w)22!x2+⋯+(∑w)nn!xn

  所以我们要求的东西:

∑a1+a2+⋯+an=n−2−k∏ni=1waii∏ni=1ai!=(∑w)n−2−k(n−2−k)!∑a1+a2+⋯+an=n−2−k∏i=1nwiai∏i=1nai!=(∑w)n−2−k(n−2−k)!

  综合一下,最后的答案是:

w1⋯wn×(n−2)!×⎛⎝∑k=0n−2⎛⎝∑{ap}wp1wp2⋯wpk×(∑w)n−2−k(n−2−k)!⎞⎠⎞⎠w1⋯wn×(n−2)!×(∑k=0n−2(∑{ap}wp1wp2⋯wpk×(∑w)n−2−k(n−2−k)!))

  除了 ∏wpi∏wpi 用 DP(看代码吧),其它部分直接算就可以了。

参考代码

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef int INT_PUT;
INT_PUT readIn()
{
INT_PUT a = 0; bool positive = true;
char ch = getchar();
while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
if (ch == '-') { positive = false; ch = getchar(); }
while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
return positive ? -a : a;
}
void printOut(INT_PUT x)
{
char buffer[20]; int length = 0;
if (x < 0) putchar('-'); else x = -x;
do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
do putchar(buffer[--length]); while (length);
}

const LL mod = int(1e9) + 7;
const int maxn = 2005;
int n;
int w[maxn];
LL temp;
LL ans;
LL mul = 1;
LL sum;

LL inv[maxn];
LL invFac[maxn];
LL power[maxn];

LL f[maxn][maxn];

void run()
{
n = readIn();
for (int i = 1; i <= n; i++)
{
w[i] = readIn();
mul = mul * w[i] % mod;
sum = (temp = sum + w[i]) >= mod ? temp - mod : temp;
}
inv[1] = 1;
for (int i = 2; i <= n; i++)
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
invFac[0] = 1;
for (int i = 1; i <= n; i++)
invFac[i] = invFac[i - 1] * inv[i] % mod;
power[0] = 1;
for (int i = 1; i <= n; i++)
power[i] = power[i - 1] * sum % mod;

f[0][0] = 1;
for (int i = 1; i <= n; i++)
{
f[i][0] = 1;
for (int j = 1; j <= i; j++)
f[i][j] = (f[i - 1][j] + f[i - 1][j - 1] * w[i]) % mod;
}

LL ans = 0;
for (int k = 0; k <= n - 2; k++)
ans = (ans + power[n - 2 - k] * invFac[n - 2 - k] % mod * f
[k]) % mod;

ans = ans * mul % mod;
for (int i = 2; i <= n - 2; i++)
ans = ans * i % mod;

printOut(ans);
}

int main()
{
#ifndef LOCAL
freopen("hehe.in", "r", stdin);
freopen("hehe.out", "w", stdout);
#endif
run();
return 0;
}


总结

  记住考虑对答案的贡献,看看怎么考虑式子最有规律。

  类似于树的度序列这样的东西可以考虑搞一些 -1 s 操作。

  生成函数大法好……这个地方只能结合公式看(背住公式:ex=1+x1!+x22!+x33!+⋯ex=1+x1!+x22!+x33!+⋯),然后也就好说了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: