「题解」Codeforces 1554E You

「题解」Codeforces 1554E You

问题可以转化成给每个边定向,ai 变成了 i 点的入度。

因为给每个点定向必然有个点的入度为 0,从这个点开始删即为一种合法删点方案;对于每个删点方案,每删一个点就把相邻的边都定向到自己,这样本质不同的删点方案一定当且仅当存在一个点的入度不同。这样构造了一个双射,完成了问题的转化。

所以答案的总和为 2n1

入度的总数为 n1,所以一定只有 n1 的因数答案才不为 0

考虑如果 k>1,怎样判断答案是不是 0?叶子是可以直接确定边要往上的,因为不能出现入度为 1,这样可以把叶子删去,删去叶子之后的叶子,挂着它的边也能定向,然后这个叶子也可以删去...这样不断做拓扑排序,即可以构造出一个方案,看这个方案满不满足条件即可。

注意到这个方案数唯一的,所以 k>1 时直接拓扑排序来判断即可。

直接这么做时间复杂度是 O(nn),有一个显著的优化是如果只判素因子就行,再把判素因子得到的 gcd 的 ans 加上 1

k=1 的答案的话,用答案的总数 2n1 减去其它所有的答案就行。复杂度是 O(nω(n))ω(n)n 的素因子个数。

#include<iostream>
#include<cstdio>
#include<queue>
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T& read(T& r) {
	r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
	while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
	return r = w ? -r : r;
}
typedef long long ll;
const ll mod = 998244353;
ll qpow(ll x, ll y) {
	ll sumq = 1;
	while(y) {
		if(y & 1) sumq = sumq * x % mod;
		 x = x * x % mod;
		 y >>= 1;
	}return sumq;
}
#define int long long 
const int N = 1000010;
int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
int n, head[N], ent, ans[N], a[N], in[N], in_[N], b[N];
bool vis[N];
struct Edge {
	int to, next;
}e[N << 1];
inline void add(int x, int y) {
	e[++ent].to = y; e[ent].next = head[x]; head[x] = ent;
}
std::queue<int>q;
int check(int x) {
	for(int i = 1; i <= n; ++i) b[i] = a[i] = 0, in[i] = in_[i], vis[i] = 0;
	for(int i = 1; i <= n; ++i)
		if(in[i] == 1) {
			q.push(i);
		}
	while(!q.empty()) {
		int y = q.front();q.pop(); vis[y] = 1;
		for(int i = head[y]; i;i = e[i].next) {
			int v = e[i].to;
			if(vis[v] == 1) continue ;
			if(a[y] == 0) ++a[v], a[v] %= x, ++b[v];
			else ++a[y], a[y] %= x, ++b[y];
			--in[v];
			if(in[v] == 1) {
				q.push(v);
			}
		}
	}
	int g = b[1];
	for(int i = 2; i <= n; ++i) g = gcd(g, b[i]);
	return g;
}
void solve() {
	read(n);
	for(int i = 1; i <= n; ++i) ans[i] = in_[i] = 0, head[i] = 0;
	ent = 0;
	for(int i = 1, u, v; i < n; ++i) read(u), read(v), add(u, v), add(v, u), in_[u]++, in_[v]++;
	int m = n-1;
	for(int i = 1; i * i <= m; ++i)
		if(m % i == 0) {
			int t = check(i);
			if(t % i == 0) ans[t] = 1;
			while(i != 1 && m % i == 0) m /= i;
		}
	if(m > 1) {
		int t = check(m);
		if(t % m == 0) ans[t] = 1;
	}
	ans[1] = qpow(2, n-1);
	for(int i = 2; i <= n; ++i) ans[1] -= ans[i];
	for(int i = 1; i <= n; ++i) printf("%lld ", ans[i]);
	puts("");
}
signed main() {
	int T; read(T);
	while(T--)
		solve();
	return 0;
}
posted @   do_while_true  阅读(38)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

This blog has running: 1845 days 1 hours 33 minutes 40 seconds

点击右上角即可分享
微信分享提示