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(看代码吧),其它部分直接算就可以了。
参考代码
总结
记住考虑对答案的贡献,看看怎么考虑式子最有规律。
类似于树的度序列这样的东西可以考虑搞一些 -1 s 操作。
生成函数大法好……这个地方只能结合公式看(背住公式:ex=1+x1!+x22!+x33!+⋯ex=1+x1!+x22!+x33!+⋯),然后也就好说了。
考场上的思路
正解
参考代码
总结
传送门
考场上的思路
想到了 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!+⋯),然后也就好说了。
相关文章推荐
- 【JZOJ5180】【NOI2017模拟6.29】呵呵
- 【TC_SRM697 Hard】【JZOJ5180】ConnectedStates 题解
- 呵呵
- jzoj3893 画矩形
- 呵呵
- [JZOJ4695]【GDOI2017模拟8.14】佐助的难题
- 呵呵,又在这里开了个博客,现在已经有好几个了
- jzoj 3577. 【CEOI2011】Traffic
- 呵呵
- jzoj 4540. 【NOI2016模拟6.12】assign
- 呵呵,开张了
- B组 JZOJ【中山市选2009】小明的游戏
- 发工资了。呵呵。
- OSI参考模型呵呵TCP/IP参考模型
- 呵呵
- 【JZOJ3990】分配
- 原来刘建超也搞笑 呵呵
- JZOJ 3984 宝石纪念币
- 呵呵,原来快车也挺好的
- [JZOJ5527]. 【清华冬令营2018模拟】 Silly