[HNOI2015]落忆枫音

Description

Luogu3244
BZOJ4011

Solution

如果是一个DAG,那么答案显然是

\[\prod_{i=2}^n deg(i) \]

其中\(deg(i)\)是点\(i\)的入度。加上条边后可能会成环,我们要去掉成环的情况数。考虑一个环,错误的计数只会发生在环上每个点选了自己环上的父亲,环外的点随意。所以对于每个环,多余的计数是

\[\prod_{i\notin Circle} deg(i) \]

而环又是一个\(y\)\(x\)的路径加上\((x,y)\)这条边构成的。所以设\(f[u]\)为从\(u\)出发到\(x\)的路径(假设其在环上)多余的贡献,那么有

\[f[u] = \sum_{(u,v)\in E} f[v] / deg[u] \]

所以就是一个简单的记搜就行了。

Code

#include <cstdio>
#include <cstring>

typedef long long ll;
const int N = 100010;
const int M = 200010;
const ll mod = 1e9 + 7;

int hd[N], nxt[M], to[M], cnt;
ll deg[N], f[N], inv[N], n, m;

inline void adde(int x, int y) {
  to[++cnt] = y;
  nxt[cnt] = hd[x];
  hd[x] = cnt;
}

int dfs(ll x) {
  if (f[x] != -1) return f[x];
  f[x] = 0;
  for (int i = hd[x]; i; i = nxt[i]) {
    f[x] = (f[x] + dfs(to[i])) % mod;
  }
  f[x] = f[x] * inv[deg[x]] % mod;
  return f[x];
}

void calc() {
  inv[1] = 1;
  for (int i = 2; i <= n; ++i)
    inv[i] = (-mod / i * inv[mod % i] % mod + mod) % mod;
}

int main() {
  int x, y;
  scanf("%lld%lld%d%d", &n, &m, &x, &y);
  for (int i = 1, x, y; i <= m; ++i) {
    scanf("%d%d", &x, &y);
    adde(x, y);
    deg[y]++;
  }
  ll ans = 1, sum = 1;
  for (int i = 2; i <= n; ++i) {
    if (i == y) ans = ans * (deg[i]+1) % mod;
    else ans = ans * deg[i] % mod;
    sum = sum * deg[i] % mod;
  }
  memset(f, -1, sizeof f); calc(); f[x] = sum * inv[deg[x]] % mod;
  ll del = dfs(y);
  ans = (ans - del + mod) % mod;
  printf("%lld\n", ans);
  return 0;
 }
posted @ 2018-10-15 19:50  wyxwyx  阅读(193)  评论(0编辑  收藏  举报