CF edu118F 题解

Problem - F - Codeforces

题意:给一棵树染色染一个排列,要求整棵树内不存在儿子染色的值是父亲值-1,求方案数。

显然可以上容斥,考虑树里面至少有k个儿子染色的值是父亲值-1,考察当前这k个儿子向父亲的边,发现这些边在树上构成一些链,而贡献的方案数有奇妙的性质:每一条链内部大小关系是确定的,即从最浅到最深依次-1,而各链、各未选中点之间的大小关系是不确定的,此时我们发现方案数就是(链数+未选中点数)的阶乘,因为我们只要固定彼此之间的大小关系就可以唯一对应原问题的一个排列。

此时相当于容斥的贡献只和我们选的k有关,即选k个贡献为(n - k)!,而树上一个点x选一个儿子有出度deg(x)个选法,sigma deg(x) = 2n, 只要分治fft即可。

//#define LOCAL
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define sc second
#define pb push_back
#define ll long long
#define trav(v,x) for(auto v:x)
#define all(x) (x).begin(), (x).end()
#define VI vector<int>
#define VLL vector<ll>
#define pll pair<ll, ll>
#define double long double
//#define int long long
using namespace std;
const int N = 1e6 + 100;
const int inf = 1e9;
//const ll inf = 1e18
const ll mod = 998244353;//1e9 + 7

#ifdef LOCAL
void debug_out(){cerr << endl;}
template<typename Head, typename... Tail>
void debug_out(Head H, Tail... T)
{
	cerr << " " << to_string(H);
	debug_out(T...);
}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif

ll qpow(ll x, ll y = mod - 2)
{
	ll res = 1;
	while(y)
	{
		if(y & 1)
			res = res * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return res;
}

namespace Poly
{
	int wh[N], cc, len;
	void ntt(VLL &a, bool inv)
	{
		for(int i = 0; i < len; i++)
		{
			if(i < wh[i])
				swap(a[i], a[wh[i]]);
		}
		for(int l = 2, md; l <= len; l <<= 1)
		{
			md = l >> 1;
			ll tmp = qpow(3, (mod - 1) / l);
			for(int i = 0; i < len; i += l)
			{
				ll mo = 1;
				for(int j = 0; j < md; j++, mo = mo * tmp % mod)
				{
					ll ha = mo * a[i + j + md] % mod;
					a[i + j + md] = (a[i + j] - ha + mod) % mod;
					a[i + j] = (a[i + j] + ha) % mod;
				}
			}
		}
		if(inv)
		{
			ll tmp = qpow(len);
			for(int i = 1; i < len / 2; i++)
				swap(a[i], a[len - i]);
			for(int i = 0; i < len; i++)
				a[i] = a[i] * tmp % mod;
		}
	}
	VLL mul(VLL x, VLL y)
	{
		int ed = x.size() + y.size() - 1;
		cc = 0, len = 1;
		while(len <= ed)
			++cc, len <<= 1;
		for(int i = 1; i < len; i++)
		{
			wh[i] = (wh[i >> 1] >> 1) | (i & 1) << (cc - 1);
		}
		x.resize(len);
		y.resize(len);
		ntt(x, 0);
		ntt(y, 0);
		for(int i = 0; i < len; i++)
		{
			x[i] = x[i] * y[i] % mod;
		}
		ntt(x, 1);
		x.resize(ed);
		return x;
	}
}

ll fac[N];
int n, deg[N];
VI adj[N];

VLL buk[N];

void dfs(int x, int ff)
{
	trav(v, adj[x])
	{
		if(v == ff)
			continue;
		dfs(v, x);
		++deg[x];
	}
	buk[x].pb(1);
	if(deg[x])
		buk[x].pb(deg[x]);
	//cerr << x << ' ' << deg[x] << ' ' << buk[x].size() << '\n';
}

VLL dac(int l, int r)
{
	if(l == r)
		return buk[l];
	int mid = l + r >> 1;
	VLL ls = dac(l, mid), rs = dac(mid + 1, r);
	return Poly::mul(ls, rs);
}

void sol()
{
	cin >> n;
	fac[0] = 1;
	for(int i = 1; i <= n; i++)
	{
		fac[i] = fac[i - 1] * i % mod;
	}
	for(int i = 1; i < n; i++)
	{
		int x, y;
		cin >> x >> y;
		adj[x].pb(y);
		adj[y].pb(x);
	}
	dfs(1, 0);
	VLL g = dac(1, n);
	ll ans = 0;
	ll coef = 1;
	for(int i = 0; i < g.size(); i++)
	{
		ll nw = coef * g[i] % mod * fac[n - i] % mod;
		ans = (ans + nw) % mod;
		coef = mod - coef;
	}
	cout << ans << '\n';
}

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
//	int tt;
//	cin >> tt;
//	while(tt--)
		sol();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值