Problem Description
Namesolo has fallen in love with the game Stick Fight. But when he is playing the game, he wonders how to find out the number of stickmen in the game.
In this question, we define the stick man as shown in the picture above. It has a chain of length 11 as the head, two chains of length 22 as the arms, a chain of length 11 as the body, and two chains of length 11 as the legs. For example, the red part in this picture can be viewed as a stick man, with chain (2,3)(2,3) to be the head, chains (3,4,6)(3,4,6) and (3,9,10)(3,9,10) to be the arms, chain (3,5)(3,5) to be the body, and chains (5,7)(5,7) and (5,8)(5,8) to be the legs.
The game can be viewed as a tree, and Namesolo wants to know how many stickmen are there. Please note that two stickmen are different when there is at least one different edge between the two edge sets that make up the two stickmen.
Because the answer may be too large, Namesolo wants to know the answer modulo 998244353998244353.
Input
The first line of input contains one integer T\ (1\le T\le 15)T (1≤T≤15), indicating the number of test cases.
For each test case, the first line contains an integer nn (1\le n \le 5\times 10^51≤n≤5×105), indicating the number of vertices in the tree. Each of the following n-1n−1 lines contains two integers a,ba,b (1 \le a,b \le n1≤a,b≤n), indicating that there is an edge connecting aa and bb in the tree.
It is guaranteed that the sum of nn over all cases won't exceed 3 \times 10^63×106.
Output
For each test case, output an integer representing the answer modulo 998244353998244353.
Sample Input
1
9
1 2
2 3
3 4
2 5
5 6
2 7
7 8
7 9
Sample Output
1
题意: 给出一棵树的结构,问这棵树中存在几个火柴人,火柴人的结构如上图中红色标示。
分析: 可以枚举火柴人的躯干,也就是图中点3到点5的那条边,设x为靠近头的端点,y为靠近腿的端点,腿的方案数就是一个组合数,从y的度数-1中挑2个点,手的方案数稍复杂一些,首先要求出到x点距离为2的点个数,这个值就是x相邻点的度数-1然后加和,然后还需要剔除在腿上的点,也就是再减掉y的度数-1个点,从这些点中选取2个点作为两只手,不过要注意还得减去两只手来自同一只手臂的情况,然后就剩下头的方案数了,这个比较容易得到,只需要在x相邻点中剔除两手臂和腿这三点,也就是x度数-3。最后把腿、手和头的方案数乘起来就是答案。
具体代码如下:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define int long long
- #define pii pair
- using namespace std;
-
- vector<int> tr[500005];
- pii e[500005];
- int a[500005];//ai记录第i点周围点的度数-1之和
- int b[500005];//bi记录第i点周围点C(度数-1,2)之和
- const int mod = 998244353;
-
- int ksm(int base, int power){
- int ans = 1;
- while(power){
- if(power&1) ans = ans*base%mod;
- base = base*base%mod;
- power >>= 1;
- }
- return ans;
- }
-
- int C(int x){//返回C(x, 2)
- if(x < 2) return 0;
- return x*(x-1)%mod*ksm(2, mod-2)%mod;
- }
-
- signed main()
- {
- int T;
- cin >> T;
- while(T--){
- int n;
- cin >> n;
- for(int i = 1; i <= n; i++){
- tr[i].clear();
- a[i] = b[i] = 0;
- }
- for(int i = 1; i < n; i++){
- scanf("%lld%lld", &e[i].first, &e[i].second);
- tr[e[i].first].push_back(e[i].second);
- tr[e[i].second].push_back(e[i].first);
- }
- //预处理
- for(int i = 1; i <= n; i++){
- for(int j = 0; j < tr[i].size(); j++){
- int to = tr[i][j];
- a[i] = (a[i]+tr[to].size()-1)%mod;
- b[i] = (b[i]+C(tr[to].size()-1))%mod;
- }
- }
- int ans = 0;
- for(int i = 1; i < n; i++){//枚举每条边
- int x = e[i].first;
- int y = e[i].second;
- int leg = C(tr[y].size()-1);//腿的方案数
- int hand = C(((a[x]-(tr[y].size()-1))%mod+mod)%mod);//手的方案数
- hand = ((hand-(b[x]-C(tr[y].size()-1)))%mod+mod)%mod;
- int head = tr[x].size()-3;//头的方案数
- if(head < 0) head = 0;
- ans = (ans+leg*hand%mod*head%mod)%mod;
- //另一个方向的边
- swap(x, y);
- leg = C(tr[y].size()-1);//腿的方案数
- hand = C(((a[x]-(tr[y].size()-1))%mod+mod)%mod);//手的方案数
- hand = ((hand-(b[x]-C(tr[y].size()-1)))%mod+mod)%mod;
- head = tr[x].size()-3;//头的方案数
- if(head < 0) head = 0;
- ans = (ans+leg*hand%mod*head%mod)%mod;
- }
- cout << ans << endl;
- }
- return 0;
- }