[组合计数]Counting Stickmen 2022杭电多校第7场 1003

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 <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#define int long long
#define pii pair<int, int>
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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值