Description
「恒逸,你相信灵魂的存在吗?」
Input
输入文件的第一行包含四个整数 n、m、x和y,依次代表枫叶上的穴位数、脉
Output
输出一行,为添加了从穴位 x连向穴位 y的脉络后,枫叶上以穴位 1 为根的脉
Sample Input
1 2
1 3
2 4
3 2
Sample Output
HINT
对于所有测试数据,1 <= n <= 100000,n - 1 <= m <= min(200000, n(n – 1) / 2),
题解
首先我们简化这个问题,考虑一个$DAG$中树形图的个数。
值得注意的是这里有朱刘算法的一个推广:
如果除根节点外每个点都选择一条入边,由于没有环,因此一定会形成一个树形图。
这里简要证明一下:
首先证明这个图是联通的。
采用反证法,假设图不联通,显然原图存在一个子图满足:子图联通,并且子图中边数$w$>点数$u$。
那么这样这个子图的总入度>总点数。那么一定存在一个点入度>$1$。与题设不符,原命题成立。
下证这个连通图满足树形图的性质。
依旧采用反证法,我们假设选择满足题意的边不能够成树形图。那么存在两条不同的路径$S_1$,$S_2$从根节点$1$到达另外一个节点$v$。
假设子图$g(u‘,w‘) ⊂$ 原图$G(u,w)$是满足$S_1,S_2 ⊂ g(u‘,w‘)$的极小子图。
既然存在两条不同的路径,显然$u‘ <= w‘$。
又由于除根节点外每个点有且仅有一个入边,显然$w‘ = u‘-1$。矛盾,原命题成立。
由上述结论,显然简化版的问题答案就是:$$ans = \prod_{i=2}^n degree_i(其中degree_i表示节点i的入度)$$
我们再来考虑原问题,现在加上额外的一条边之后,原图可能形成环。再由上面的决策,显然不可行。
我们考虑不合法的情况是什么:显然选边的时候构成环就是不合法的。并且这个环中的一条边肯定是$x \rightarrow y$。
容易得到的结论就是,需要减去的不合法的值就是$$\sum_{S是G中y \rightarrow x的一条路径的点集}\prod_{2\leq j\leq n,j\notin S}degree_j$$
考虑贡献法来计算,用$top-sort-dp$,记$f_i$表示$$\sum_{S是G中y \rightarrow i的一条路径的点集}\prod_{2\leq j\leq n,j\notin S}degree_j$$
$dp$方程就是$$f_i=\frac{\sum_{j\rightarrow i}f_j}{degree_i}$$
初值$$f_y=\frac{\prod_{i=2}^ndegree_i}{degree_y}$$
由于$1$号根节点不选入边,所以$1$号点显然不能构成环。若$y == 1$,显然这条边不可能被选,不用$top-sort-dp$,直接输出原始$ans$即可。
1 //It is made by Awson on 2017.12.24 2 #include <map> 3 #include <set> 4 #include <cmath> 5 #include <ctime> 6 #include <queue> 7 #include <stack> 8 #include <vector> 9 #include <cstdio> 10 #include <string> 11 #include <cstdlib> 12 #include <cstring> 13 #include <iostream> 14 #include <algorithm> 15 #define LL long long 16 #define Max(a, b) ((a) > (b) ? (a) : (b)) 17 #define Min(a, b) ((a) < (b) ? (a) : (b)) 18 using namespace std; 19 const int N = 100000; 20 const int M = 200000; 21 const int MOD = 1000000007; 22 23 int n, m, x, y, u, v; 24 struct tt { 25 int to, next; 26 }edge[M+5]; 27 int path[N+5], top; 28 int ans = 1, degree[N+5], in[N+5]; 29 int f[N+5]; 30 queue<int>Q; 31 32 int quick_pow(int a, int b) { 33 int cnt = 1; 34 while (b) { 35 if (b&1) cnt = (LL)cnt*a%MOD; 36 a = (LL)a*a%MOD; 37 b >>= 1; 38 } 39 return cnt; 40 } 41 void topsort() { 42 f[y] = ans; 43 for (int i = 1; i <= n; i++) if (in[i] == 0) Q.push(i); 44 while (!Q.empty()) { 45 int u = Q.front(); Q.pop(); 46 f[u] = (LL)f[u]*quick_pow(degree[u], MOD-2)%MOD; 47 for (int i = path[u]; i; i = edge[i].next) { 48 (f[edge[i].to] += f[u]) %= MOD; in[edge[i].to]--; 49 if (in[edge[i].to] == 0) Q.push(edge[i].to); 50 } 51 } 52 } 53 void add(int u, int v) { 54 edge[++top].to = v; 55 edge[top].next = path[u]; 56 path[u] = top; 57 } 58 void work() { 59 scanf("%d%d%d%d", &n, &m, &x, &y); 60 degree[y]++; 61 for (int i = 1; i <= m; i++) { 62 scanf("%d%d", &u, &v); 63 add(u, v); degree[v]++; 64 } 65 for (int i = 2; i <= n; i++) ans = (LL)ans*degree[i]%MOD; 66 for (int i = 1; i <= n; i++) in[i] = degree[i]; in[y]--; 67 if (y != 1) topsort(); 68 printf("%d\n", (ans-f[x]+MOD)%MOD); 69 } 70 int main() { 71 work(); 72 return 0; 73 }