【YbtOJ#20064】预算缩减

题目

题目链接:http://noip.ybtoj.com.cn/contest/90/problem/2
给定一棵树,你需要删去一些边(可以不删),使得剩下的图中每个点所在的连通块大小都 m

求删边的方案数,对 786433 取模。两种方案不同,当且仅当存在一条边在一个方案中被删去,而在另外一个方案中没有被删去。

思路

f[x][i] 表示在 x 子树中,x 所在连通块大小为 i,其他联通块大小全部不小于 m 的方案数。
考虑加入一棵子树 y 的时候,有

f[x][i+j]=f[x][i]+f[x][i]×f[v][j]

f[x][i]=f[x][i]+f[x][i]×f[v][j] (jm)

其中变量只需要枚举到子树大小,这样复杂度等价于枚举了子树的两个点,而每两个点在 LCA 处才会被枚举。时间复杂度 O(n2)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=5010,MOD=786433;
int n,m,tot,head[N],size[N];
ll ans,f[N][N],g[N];

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
}

void dfs(int x,int fa)
{
	size[x]=f[x][1]=1;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=fa)
		{
			dfs(v,x);
			for (int j=1;j<=size[x]+size[v];j++)
				g[j]=f[x][j],f[x][j]=0;
			for (int j=1;j<=size[x];j++)
				for (int k=1;k<=size[v];k++)
				{
					f[x][j+k]=(f[x][j+k]+f[v][k]*g[j])%MOD;
					if (k>=m) f[x][j]=(f[x][j]+f[v][k]*g[j])%MOD;
				}
			size[x]+=size[v];
		}
	}
}

int main()
{
	freopen("cut.in","r",stdin);
	freopen("cut.out","w",stdout);
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1,x,y;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	dfs(1,0);
	for (int i=m;i<=n;i++)
		ans=(ans+f[1][i])%MOD;
	printf("%lld",ans);
	return 0;
}
posted @   stoorz  阅读(145)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· DeepSeek火爆全网,官网宕机?本地部署一个随便玩「LLM探索」
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 上周热点回顾(1.20-1.26)
· 【译】.NET 升级助手现在支持升级到集中式包管理
点击右上角即可分享
微信分享提示