G. Counting Graphs

发布时间 2023-08-08 23:21:35作者: onlyblues

G. Counting Graphs

Given a tree consisting of $n$ vertices. A tree is a connected undirected graph without cycles. Each edge of the tree has its weight, $w_i$.

Your task is to count the number of different graphs that satisfy all four conditions:

  1. The graph does not have self-loops and multiple edges.
  2. The weights on the edges of the graph are integers and do not exceed $S$.
  3. The graph has exactly one minimum spanning tree.
  4. The minimum spanning tree of the graph is the given tree.

Two graphs are considered different if their sets of edges are different, taking into account the weights of the edges.

The answer can be large, output it modulo $998244353$.

Input

The first line contains an integer $t$ ($1\le t\le 10^4$) — the number of test cases.

The first line of each test case contains two integers $n$ and $S$ ($2 \le n \le 2 \cdot 10^5, 1\le S\le 10^9$) — the number of vertices and the upper bound of the weights.

Then follow $n-1$ lines describing the tree, the $i$-th line contains three integers $u_i$, $v_i$, and $w_i$ ($1\le u_i,v_i\le n, u_i \ne v_i, 1\le w_i\le S$) — an edge in the tree with weight $w_i$.

It is guaranteed that the sum of $n$ for all tests does not exceed $2\cdot 10^5$.

Output

For each test, output the number of different graphs that satisfy the conditions, modulo $998244353$.

Example

input

4
2 5
1 2 4
4 5
1 2 2
2 3 4
3 4 3
5 6
1 2 3
1 3 2
3 4 6
3 5 1
10 200
1 2 3
2 3 33
3 4 200
1 5 132
5 6 1
5 7 29
7 8 187
7 9 20
7 10 4

output

1
8
80
650867886

Note

In the first sample, there is only one graph, which is the given tree.

In the second samle, the given tree looks like this:

All possible graphs for the second sample are shown below, the minimum spanning tree is highlighted in red:

 

解题思路

  问题就是让我们在原有的树中加一些额外的边,这些边不能是自环或重边,且边的权值不超过$S$,然后对构造的图求最小生成树,问有多少个这样的图所得到的最小生成树就是原来的树?

  先考虑加一条边,选择两个节点$u$和$v$相连,当然$u$和$v$不能直接相邻否则就会有重边。此时就会形成一个环,假设在$u$和$v$相连前,从$u$到$v$的简单路径中最大的权值为$P(u,v)$,根据最小生成树的性质,这条新边的权值应该在$[P(u,v)+1, S]$这个区间内,这样构造的新图的最小生成树才是原来的那棵树。否则在这个环中就至少存在一条边的权值比新边大或相等,那么保留新边并选择一条权重比新边大或相等的边删去,得到一棵新树,可以知道这棵树的权重比原来的小或相等,最小生成树就肯定不是原来那棵树了。事实上只要新加的边满足上面的条件,那么构造的新图的最小生成树就一定是原来的树,因此加边及赋权值的行为可以看作是相互独立的。

  由于加的是无向边,为了避免重复这里规定从编号小的节点向编号大的节点连一条边,由于加边是独立的,结合乘法原理,那么满足要求的图的数量就是$\prod\limits_{1\le u < v \le n}{} (S-P(u,v)+1)$,这其中考虑了某两个点不连边的情况,因此任意两个点间连边的情况就有$S-P(u,v)+1$种(当然还要保证$u$和$v$不直接相连)。可以发现如果直接暴力求的话时间复杂度至少是$O(n^2 \log{n})$,这里不好直接优化(因为要双重循环枚举$u$和$v$),因此尝试能不能用另外的方法求出这个值。

  可以发现任意两点$u$和$v$连边后赋值的受限取决于原树中这两点简单路径上的最大权值$P(u,v)$,在$u$和$v$相连后,与原树形成的环就一定包含这条权值为$P(u,v)$的边,因此可以根据$P(u,v)$来对所有的新边进行分类,即根据形成的环所包含的最大权值的边进行分类。那么我们可以先对原树中的边按照权值从小到大排序,并且把原树中的所有边删除,然后再从小到大依次枚举每一条边并重新加入到树中。假设当前枚举到的第$i$条边$(u_i, v_i, w_i)$,若要以这条边作为环中最大权值的边,那么很明显要包含这条边,且其他边的权值不能超过$w_i$。由于我们是从小到大往树中加边,那么我们就可以从与$u_i$连通的点集,以及与$v_i$连通的点集中分别选出一个点,然后这两个点相连形成新边(根据树的性质当枚举到第$i$条边时$u_i$和$v_i$不连通)。假设与$u_i$连通的点集大小为${sz}_{u_{i}}$,与$v_i$连通的点集大小为${sz}_{v_{i}}$,那么根据乘法原理在形成的环中最大权值为$w_i$的新边数量就是${sz}_{u_{i}} \cdot {sz}_{v_{i}} - 1$,减$1$是删去$u_i$和$v_i$这两个直接相连的点所构成的新边。又由于这${sz}_{u_{i}} \cdot {sz}_{v_{i}} - 1$条新边中每条边都有$S-w_i+1$种选择,由独立性因此一共有${(S-w_i+1)^{{sz}_{u_{i}} \cdot {sz}_{v_{i}} - 1}}$种情况。

  可能你会问如果环中权值最大的边有多条怎么办?上面的做法会不会重复统计?答案是不会的,如果存在多条取值最大的边,那么我们就只认在排序后最靠前的边作为唯一的权值最大的边,当枚举完这条边后,这条边两个端点所在的连通块就会合并成一个,而对应的新边的两个点往后就不会再被枚举到(因为每次都是从两个不同的连通块中选点)。

  再考虑完所有不同$P(u,v)$的情况,那么最终的答案就是$$\prod\limits_{i=1}^{n-1}{{(S-w_i+1)^{{sz}_{u_{i}} \cdot {sz}_{v_{i}} - 1}}}$$

  AC代码如下,时间复杂度为$O(n \log{n})$:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 2e5 + 10, mod = 998244353;
 7 
 8 struct Node {
 9     int v, w, wt;
10 }e[N];
11 int fa[N], sz[N];
12 
13 int find(int x) {
14     return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
15 }
16 
17 int qmi(int a, LL k) {
18     int ret = 1;
19     while (k) {
20         if (k & 1) ret = 1ll * ret * a % mod;
21         a = 1ll * a * a % mod;
22         k >>= 1;
23     }
24     return ret;
25 }
26 
27 void solve() {
28     int n, m;
29     scanf("%d %d", &n, &m);
30     for (int i = 0; i < n - 1; i++) {
31         scanf("%d %d %d", &e[i].v, &e[i].w, &e[i].wt);
32     }
33     sort(e, e + n - 1, [&](Node &a, Node &b) {
34         return a.wt < b.wt;
35     });
36     for (int i = 1; i <= n; i++) {
37         fa[i] = i;
38         sz[i] = 1;
39     }
40     int ret = 1;
41     for (int i = 0; i < n - 1; i++) {
42         int x = find(e[i].v), y = find(e[i].w);
43         ret = 1ll * ret * qmi(m - e[i].wt + 1, (1ll * sz[x] * sz[y] - 1)) % mod;
44         sz[y] += sz[x];
45         fa[x] = y;
46     }
47     printf("%d\n", ret);
48 }
49 
50 int main() {
51     int t;
52     scanf("%d", &t);
53     while (t--) {
54         solve();
55     }
56     
57     return 0;
58 }

 

参考资料

  Codeforces Round 891 (Div. 3) Editorial:https://codeforces.com/blog/entry/119134