【HDU 5909】Tree Cutting

题目描述:

  给你一棵n个节点的带点权树。每个点的点权都小于m,m为某个2的整次方数,求树上所有联通块点集点权异或和为0-m-1的各有多少个。

 对于15%的数据n<=18;

对于另外20%的数据:n<=30;

对于另外25%的数据:n,m<=300;

对于100%的数据,n,m<=1024;

15%做法:

搜索,暴力枚举点集,判断是否是联通块,是就算贡献,这里就不细讲了。

35%做法:

注意到要求是联通块,所以搜索选点的时候加点优化,保证每一步都是联通的即可。

60%做法:

考虑在树上dp,任意选根,设f【i】【j】表示在i的子树中选取联通块,且异或和为j的方案数,g【i】【j】为在i的子树中选取联通块,必定选点i,且异或和为j的方案数。可以发现,对于i的子树考虑,f【i】【j】=g【i】【j】+f【k1】【j】+f【k2】【j】+f【k3】【j】......;其中(k1,k2,k3....为i的所有儿子),那么怎么求g【i】【j】呢,考虑合并答案:

【HDU 5909】Tree Cutting_第1张图片

如图,考虑将i的子树的答案一个个合并到i里面,我们按照k1,k2,k3的顺序合并答案,例如合并完k2子树的答案时, g【i】【j】暂时存的值就是选i点,以及可以在k1,k2两棵子树选点的构成的联通块的方案(不考虑在子树k3中的选点情况)。而合并答案的时候,就要在原来的基础上加上在子树k3一定选的方案数:

就这样合并答案即可,时间复杂度:

100%的做法:

我们考虑点分治,将每次要操作的重心为根造出dfs序,然后求强制过根的联通块方案数,然后分成子树分治,显然,算完强制过根的方案后,再算子树内的方案,子树内的方案是必定不过根的,所以可以不重不漏的计算,接下来的难点就在于怎么进行dp。我们可以考虑将联通块的点按dfs序取点,然后对于f【i】【j】表示现在以dfs序为i的点为决策点,异或和为j的方案数。dfs序为i的点一共只有两种情况,选和不选,由于强制过根,如果不选的话,它的子树也不能选,也就是决策点可以转移到f【i+size【sequence【i】】】【j】上(其中sequence【i】为dfs序为i的节点的编号,而size存的是对应编号的节点的子树的大小):

【HDU 5909】Tree Cutting_第2张图片

【HDU 5909】Tree Cutting_第3张图片

【HDU 5909】Tree Cutting_第4张图片

这样,我们发现,每个点只有两种决策,也就是说每个点只会转移两个方向,转移过程的时间复杂度是的,而点分治只需要递归logn层,所以总的时间复杂度:

                                                     【HDU 5909】Tree Cutting_第5张图片

                                                               推荐番:《干物妹!小埋》

上代码(^-^)

#include 
#include 
#include 
#include 
using namespace std;
int pan[3005],to[3005],pp[3005],nex[3005],ans[3005],siz[3005],line[3005],v[3005],visit[3005];
int dp[1050][1050],ma,mi,p,mod,n,m,x,y,t,wei;
void dfs(int k,int sum)
{
	pan[k]=1;siz[k]=1;
	int pu=pp[k],ma=0;
	while (pu>0)
	{
		if (pan[to[pu]]==0 && visit[to[pu]]!=1)
		{
			dfs(to[pu],sum);siz[k]+=siz[to[pu]];
			ma=max(ma,siz[to[pu]]);
		}
		pu=nex[pu];
	}
	ma=max(ma,sum-siz[k]);
	if (ma0)
	{
		if (pan[to[pu]]==1 && visit[to[pu]]!=1)
		{
			dfs2(to[pu]);siz[k]+=siz[to[pu]];
		}
		pu=nex[pu];
	}
}
void zhao(int k,int large)
{
	mod=1000000007;
	mi=1000000007;p=0;
	dfs(k,large);
	dfs2(wei);
	for (int i=2;i<=large+1;i++)
	{
		for (int j=0;j>t;
	for (int o=1;o<=t;o++)
	{
		for (int i=1;i<=n;i++)visit[i]=0;
	cin>>n>>m;p=0;
	for (int i=0;i

你可能感兴趣的:(图论,点分治,动态规划)