1. 程式人生 > >【BZOJ3162】獨釣寒江雪 樹同構+DP

【BZOJ3162】獨釣寒江雪 樹同構+DP

eight pri 相同 題解 con oid src str mil

【BZOJ3162】獨釣寒江雪

技術分享

技術分享

技術分享

題解:先進行樹hash,方法是找重心,如果重心有兩個,則新建一個虛點將兩個重心連起來,新點即為新樹的重心。將重心當做根進行hash,hash函數不能太簡單,我的方法是:將x的所有兒子的hash值排序,然後將這些hash值立方合在一起作為x的hash值。

進行完樹hash後,我們考慮DP。首先不考慮同構,設f[0/1][x]表示選(不選)x時,在x的子樹中選出獨立集的方案數,則有

$f[0][x]=\prod f[1][y]+f[0][y]\\f[1][x]=\prod f[0][y]$

考慮同構,如果x有m個兒子是相同的,它們的f值都是s,那麽可以轉化成如下問題:給m個相同球染s種顏色有多少種方案,顯然答案=$C_{m+s-1}^m$。

如果根是虛點,那麽最後統計答案的時候需要特判。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxn=500010;
const ll mod=1000000007;
int n,rt,rt1,rt2,cnt;
int head[maxn],next[maxn<<1],to[maxn<<1],siz[maxn];
bool vis[maxn];
ull hs[maxn];
ll f[2][maxn],ine[maxn],jcc[maxn];
vector<int> ch[maxn];
bool cmp(int a,int b)
{
	return hs[a]<hs[b];
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
void findr(int x,int fa)
{
	siz[x]=1;
	int flag=0;
	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa)	findr(to[i],x),siz[x]+=siz[to[i]],flag|=(siz[to[i]]>(n/2));
	flag|=(n-siz[x]>(n/2));
	if(!flag&&rt1)	rt2=x;
	if(!flag&&!rt1)	rt1=x;
}
void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
void gethash(int x)
{
	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])	vis[to[i]]=1,ch[x].push_back(to[i]);
	hs[x]=ch[x].size()+1;
	if(!ch[x].size())	return ;
	for(int i=0;i<(int)ch[x].size();i++)	gethash(ch[x][i]);
	sort(ch[x].begin(),ch[x].end(),cmp);
	for(int i=0;i<(int)ch[x].size();i++)	hs[x]=hs[x]*131+hs[ch[x][i]]*hs[ch[x][i]]*hs[ch[x][i]];
}
ll calc(ll a,ll b)
{
	ll ret=1;
	for(ll i=a;i>a-b;i--)	ret=ret*i%mod;
	return ret*jcc[b]%mod;
}
void dfs(int x)
{
	f[0][x]=1,f[1][x]=1;
	ll now=0;
	for(int i=0,j;i<(int)ch[x].size();i++)
	{
		j=ch[x][i],dfs(j);
		now++;
		if(i==(int)ch[x].size()-1||hs[j]!=hs[ch[x][i+1]])
		{
			f[0][x]=f[0][x]*calc(now+f[1][j]+f[0][j]-1,now)%mod;
			f[1][x]=f[1][x]*calc(now+f[0][j]-1,now)%mod;
			now=0;
		}
	}
}
int main()
{
	n=rd();
	int i,a,b;
	memset(head,-1,sizeof(head));
	ine[1]=jcc[1]=1;
	for(i=2;i<=n;i++)	ine[i]=(mod-(mod/i)*ine[mod%i]%mod)%mod,jcc[i]=jcc[i-1]*ine[i]%mod;
	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
	findr(1,0);
	if(rt2)	add(++n,rt1),add(n,rt2),rt=n;
	else	rt=rt1;
	vis[rt]=1,gethash(rt);
	dfs(rt);
	if(rt2)
	{
		if(hs[rt1]==hs[rt2])	printf("%lld",(f[0][rt1]*(f[0][rt1]+1)/2+f[0][rt1]*f[1][rt2])%mod);
		else	printf("%lld",(f[0][rt]-f[1][rt1]*f[1][rt2]%mod+mod)%mod);
	}
	else	printf("%lld",(f[0][rt]+f[1][rt])%mod);
	return 0;
}

【BZOJ3162】獨釣寒江雪 樹同構+DP