bzoj3162 独钓寒江雪 树形dp&hash

       首先最大独立集一定是很好求的把。。

       题目中的是无根树,那么不妨转化成有根树,显然取重心作为根一般不会发生问题,因为这种情况下同构的子树形态完全相同;但是特殊情况是重心有2个的情况,如果直接取一个作为根的话会造成子树被破坏,解决方法是新建一个点作为根,向两个中心连边。

       令f[x][0]表示不取x的本质不同的最大独立集的个数,f[x][1]表示取x的本质不用的最大独立集的个数。那么对于x的所有形态形同的子树,把它们的f[]放到一起转移。具体地:

       设x的子树中某一形态的子树个数为t,且这种子树的f[][0]=a,f[][1]=b,考虑对f[x][0]的贡献(即累乘的值),对于每一个子树,一共有a+b种选择。那么如果不考虑本质不同,就有(a+b)^t种方案;如果要使得本质不同,我们不妨对a+b中选择进行编号1~a+b,然后给这t棵子树定一个顺序,强迫某一个子树选择的编号一定要大于等于前面一个子树选择的编号,显然这样的方案数就是本质不同的方案数。注意到这是一个相当经典的组合问题,只需要让第i棵子树选择的编号+i-1,那么就相当于在a+b+t-1个数中选择t个,也就是说贡献为C(a+b+t-1,t)。

       判断子树是否相同,可以使用hash;或者给同一个深度的节点,相同编号的节点所在子树完全相同,那么对于深度-1的节点就很容易得到哪些节点相同了。hash方便快捷,但可能rp爆零;后面的(vfk称之为波兰式hash)比较麻烦,而且不如hash快,但是保证正确。。

       hash用的素数一定要取得大一点。。。越大越好。不然很容易卡掉。另外hash函数可以设计得奇葩一点,并且要尽可能多的利用可用的信息。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500005
#define ll long long
#define mod 1000000007
using namespace std;

int n,tot=1,fst[N],pnt[N<<1],nxt[N<<1],sz[N],rt[2],inv[N],f[N][2],q[N];
ll hsh[N];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x,int fa){
	int p; bool ok=1; sz[x]=1;
	for (p=fst[x]; p; p=nxt[p]){
		int y=pnt[p];
		if (y!=fa){
			dfs(y,x); sz[x]+=sz[y];
			if ((sz[y]<<1)>n) ok=0;
		}
	}
	if ((sz[x]<<1)<n) ok=0;
	if (ok)
		if (rt[0]) rt[1]=x; else rt[0]=x;
}
bool cmp_hsh(int x,int y){ return hsh[x]<hsh[y]; }
int cbn(int x,int y){
	int tmp=x;
	for (; y>1; y--) tmp=(ll)tmp*(x-y+1)%mod*inv[y]%mod;
	return tmp;
}
void dp(int x,int fa){
	int p,i,j,tp=0;
	hsh[x]=20000909;	
	for (p=fst[x]; p; p=nxt[p])
		if (pnt[p]!=fa) dp(pnt[p],x);
	for (p=fst[x]; p; p=nxt[p])
		if (pnt[p]!=fa) q[++tp]=pnt[p];
	sort(q+1,q+tp+1,cmp_hsh); f[x][0]=f[x][1]=1;
	for (i=1; i<=tp; i=j){
		for (j=i+1; j<=tp; j++) if (hsh[q[i]]!=hsh[q[j]]) break;
		f[x][0]=(ll)f[x][0]*cbn(((j-i-1+f[q[i]][0])%mod+f[q[i]][1])%mod,j-i)%mod;
		f[x][1]=(ll)f[x][1]*cbn((j-i-1+f[q[i]][0])%mod,j-i)%mod;
	}
	for (i=1; i<=tp; i++) hsh[x]=hsh[x]*2010527+(hsh[q[i]]+i)*1231231237;//这里的+i效果很显著
}
int main(){
	n=read(); int i,x,y;
	for (i=1; i<n; i++){
		x=read(); y=read();
		add(x,y); add(y,x);
	}
	inv[1]=1;
	for (i=2; i<=n; i++) inv[i]=mod-(ll)inv[mod%i]*(mod/i)%mod;
	dfs(1,0); int k;
	if (rt[1]){
		for (i=fst[rt[0]]; i; i=nxt[i]){
			y=pnt[i];
			if (y==rt[1]){ pnt[i]=pnt[i^1]=k=n+1; break; }
		}
		add(k,rt[0]); add(k,rt[1]);
	} else k=rt[0];
	dp(k,0);
	if (rt[1]){
		x=rt[0]; y=rt[1];
		if (hsh[x]!=hsh[y])
			printf("%d\n",((ll)f[x][0]*f[y][0]%mod+(ll)f[x][0]*f[y][1]%mod+(ll)f[x][1]*f[y][0]%mod)%mod);
		else printf("%d\n",((ll)f[x][0]*f[x][1]%mod+cbn(f[x][0]+1,2))%mod);
	}
	else printf("%d\n",(f[k][0]+f[k][1])%mod);
	return 0;
}


by lych

2016.4.7

你可能感兴趣的:(hash,树形DP)