iai 定向 题解

题目

题意

给定一棵树,给 n − 1 n-1 n1 条边定向,求恰有 k k k 个点无出边的方案数。

解析

设计 f ( u , i , 0 / 1 ) f(u,i,0/1) f(u,i,0/1) 表示以 u u u 为根的子树内,有 i i i 个无出边, u u u 是否有出边的方案数。

树背包。

考虑转移。对 v ∈ s o n u v \in son_u vsonu,枚举 j j j,钦定 v v v 子树贡献为 j j j,则 v v v 之前贡献为 i − j i-j ij,讨论边的方向:

u → v u \to v uv

f ( u , i , 1 ) = ∑ j ( f ( u , i − j + 1 , 0 ) + f ( u , i − j , 1 ) ) × ( f ( v , j , 0 ) + f ( v , j , 1 ) ) f(u,i,1) = \sum_{j} (f(u,i-j+1,0) +f(u,i-j,1)) \times (f(v,j,0)+f(v,j,1)) f(u,i,1)=j(f(u,ij+1,0)+f(u,ij,1))×(f(v,j,0)+f(v,j,1))

v → u v \to u vu

f ( u , i , 1 ) = ∑ j f ( u , i − j , 1 ) × ( f ( v , j + 1 , 0 ) + f ( v , j , 1 ) ) f ( u , i , 0 ) = ∑ j f ( u , i − j , 0 ) × ( f ( v , j + 1 , 0 ) + f ( v , j , 1 ) ) f(u,i,1) = \sum_j f(u,i-j,1) \times (f(v,j+1,0)+f(v,j,1))\\ f(u,i,0) = \sum_j f(u,i-j,0) \times (f(v,j+1,0)+f(v,j,1)) f(u,i,1)=jf(u,ij,1)×(f(v,j+1,0)+f(v,j,1))f(u,i,0)=jf(u,ij,0)×(f(v,j+1,0)+f(v,j,1))

注意树背包滚动数组的特点,倒序枚举,每轮的 f f f 值不要继承。

#include
#define int long long
using namespace std;

const int N = 1005;
const int mod = 998244353;

vector <int> G[N];

int n, t, siz[N], f[N][N][2];

void dfs(int u, int fa)
{
	f[u][1][0] = 1, siz[u] = 1;
	for(auto v : G[u])
	{
		if(v == fa) continue;
		dfs(v, u), siz[u] += siz[v];
		for(int i=min(t, siz[u]); i>=1; i--)
		{
			int cur0 = 0, cur1 = 0;
			for(int j=0; j<=min(i, siz[v]); j++)
			{
				cur1 += (f[u][i-j][1] + f[u][i-j+1][0]) * (f[v][j][0] + f[v][j][1]) % mod, cur1 %= mod;
				cur1 += f[u][i-j][1] * (f[v][j+1][0] + f[v][j][1]) % mod, cur1 %= mod;
				cur0 += f[u][i-j][0] * (f[v][j+1][0] + f[v][j][1]) % mod, cur0 %= mod;
			}
			f[u][i][0] = cur0, f[u][i][1] = cur1;
		}
	}
}

signed main()
{
	ios::sync_with_stdio(0); cin.tie(0);
	
	cin >> n;
	
	for(int i=1; i<n; i++)
	{
		int u, v;
		cin >> u >> v;
		G[u].push_back(v), G[v].push_back(u);
	}
	
	cin >> t;
	
	dfs(1, 0);
	
	cout << (f[1][t][0] + f[1][t][1]) % mod;
}

你可能感兴趣的:(题解,c++)