DTOJ 4707. 点分治

题意

对于一棵 n n n 个点的树,求其点分治方案数。

两种点分治方案不同,当且仅当某个连通块的分治中心不同。

为了避免出现歧义,我们提供了一份暴力代码来具体描述这个算法。

const int mod=1e9+7;
const int maxn=5005;
bool vis[maxn];
vector<int> e[maxn];
int n;
inline void view_all(vector<int> &cur,int x,int fa){
	cur.push_back(x);
	for(int p: e[x]){
		if (vis[p]) continue;
		if (p == fa) continue;
		view_all(cur, p, x);
	}
}
inline int calc(int x){
	vector<int> cur;
	int ans = 0;
	view_all(cur, x, -1);
	for(int w: cur){
		int res = 1;
		vis[w] = 1;
		for(int p: e[w]){
			if (vis[p]) continue;
			res = 1ll * res * calc(p) % mod;
		}
		vis[w] = 0;
		ans = (ans + res) % mod;
	}
	return ans;
}
inline int get_ans(){
	return calc(1);
}

10 % 10\% 10% 的数据, n ≤ 5 n\le 5 n5.

40 % 40\% 40% 的数据, n ≤ 50 n\le 50 n50.

对另 20 % 20\% 20% 的数据,树为一条链.

100 % 100\% 100% 的数据, n ≤ 5000 n\le 5000 n5000.

题解

考虑树形DP,问题在于两个已经确定点分树的形态的树,合并后能形成多少种形态的点分树。
但这是一个未定义的问题,先考虑定义两颗原本相连的树 u , v u,v u,v分裂后,它们各自的点分树应该是什么样的:在点分树上把在原来的树上属于 u , v u,v u,v的点黑白染色,每个点找它同色的最近祖先,就唯一地得到两个新的点分树。
考虑有多少点分树经过这样的操作后能得到原来 u , v u,v u,v的点分树,首先原树中相邻的节点在点分树中一定有祖先后代关系,而原来点分树中 u , v u,v u,v到根的路径上的其他子树合并后形态还是确定的(因为要保证原来的祖先后代关系),不妨先把它缩起来,这样剩下 u , v u,v u,v到根的路径上的点,可以以任意次序合并。
所以只需知道 u u u在原来的点分树上的深度,记 f [ u ] [ i ] f[u][i] f[u][i] u u u在原来的点分树上深度为 i i i的方案数,按树形背包的方式合并,前缀和优化后可做到每次的效率都是两子树大小乘积,复杂度 O ( n 2 ) O(n^{2}) O(n2)
本题的突破口在于从分裂的情况(多个映射到一个)出发,定义合并时的方案数(一个对应多个),使得所有方案得以不重不漏地计算到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值