HNOI 2015 这是有多喜欢 dp ?六道题三道DP……
先考虑没有环的情况,设
di
d
i
为
i
i
的入度,那么构造一个以 为根的生成树,就相当于为除
1
1
之外的每个点选一个 father ,所以方案数为:
把上式即为 dans d a n s 。
然后考虑在有环时,如何去除不合法(形成环)的方案数。
首先,如果 y=1 y = 1 ,那么所有的情况都合法。
否则 x x 和 之间形成环的条件显然为:
存在一条从 y y 到 的路径,并且 y y 的 father 为 。
而如果已经确定了这条路径,那么这种情况下不合法的方案数为:
∑i=2ndi[i不在生成树中y到x的路径上]
∑
i
=
2
n
d
i
[
i
不
在
生
成
树
中
y
到
x
的
路
径
上
]
考虑设 rp(T) r p ( T ) ( T T 是一条的路径):
定义状态 f[u] f [ u ] 表示 ∑Trp(T)[T是一条从y到u的路径] ∑ T r p ( T ) [ T 是 一 条 从 y 到 u 的 路 径 ]
边界:
f[y]=dans×d−1y
f
[
y
]
=
d
a
n
s
×
d
y
−
1
利用拓扑排序进行转移,对于每一条边(不包括 <x,y> < x , y > <script type="math/tex" id="MathJax-Element-38"> </script>):
f[v]+=f[u]×d−1v
f
[
v
]
+
=
f
[
u
]
×
d
v
−
1
( −1 − 1 为乘法逆元)
答案就比较显然了:
dans−f[x]
d
a
n
s
−
f
[
x
]
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Edge(u) for (int e = adj[u], v; e; e = nxt[e])
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;
}
const int N = 1e5 + 5, M = 2e5 + 5, PYZ = 1e9 + 7;
int n, ecnt, d[N], nxt[M], adj[N], go[M], inv[N], cnt[N];
bool vis[N];
void add_edge(int u, int v) {
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
}
int qpow(int a, int b) {
int res = 1; while (b)
b & 1 ? res = 1ll * res * a % PYZ : 0, a = 1ll * a * a % PYZ, b >>= 1;
return res;
}
void dfs(int u) {
vis[u] = 1; Edge(u) if (!vis[v = go[e]]) dfs(v);
}
int m, X, Y, sum = 1, H, T, que[N], f[N];
int main() {
int i, x, y; n = read(); m = read(); X = read(); d[Y = read()]++;
For (i, 1, m) x = read(), y = read(), add_edge(x, y), d[y]++;
For (i, 2, n) sum = 1ll * sum * d[i] % PYZ; d[1] = 1;
For (i, 1, n) inv[i] = qpow(d[i], PYZ - 2);
H = 0; f[que[T = 1] = Y] = 1ll * sum * inv[Y] % PYZ;
dfs(Y); For (i, 1, n) Edge(i)
if (vis[i] && vis[v = go[e]]) cnt[v]++;
while (H < T) {
int u = que[++H]; Edge(u) {
if (!vis[v = go[e]]) continue;
if (!(--cnt[v])) que[++T] = v;
f[v] = (f[v] + 1ll * f[u] * inv[v] % PYZ) % PYZ;
}
}
if (Y == 1) cout << sum << endl;
else cout << (sum - f[X] + PYZ) % PYZ << endl;
return 0;
}