[HNOI2015]落忆枫音

Description

Luogu3244
BZOJ4011

Solution

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

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

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

[prod_{i otin 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
", ans);
  return 0;
 }
原文地址:https://www.cnblogs.com/wyxwyx/p/hnoi2015lysy.html