zoukankan      html  css  js  c++  java
  • BZOJ3162 独钓寒江雪

    题意

    求一棵无根树上本质不同的独立集个数。答案对$10^9+7$取模。

    (n leq 500000)

    分析

    如果只求树上独立集个数的话,设$dp[x][0/1]$表示以$x$为根的子树中选或不选$x$的方案数。有显然的转移:

    [ dp[x][0]=prod_{y in son(x)}(dp[y][0]+dp[y][1]) 就是所有子树中选与不选和的乘积 \ dp[x][1]=prod_{y in son(x)}dp[y][0] 就是所有子树中不选的乘积 ]

    现在问题在于本质不同。那么如果重新标号之后同构的话方案数就会多算。

    考虑重新标号后重心不会变,于是以重心为根处理子树。如果两个点为根是同构的,那么他们的重心相同,所以直接找出树的重心,以重心为根进行转移。

    提前预处理每一棵子树的哈希值,因为相同的子树是同构的,所以转移相当于是可重组合的计算。

    一个组合数学的相关知识:有$n$种物品要求从中选$m$个求问方案数,那么就是$inom{n+m-1}$。

    这题要求求本质不同的独立集个数,这时候就需要做的时候把所有本质相同的看作一个子树,那么就相当于在这些本质相同子树中选它们的数量那么多个,利用上面的组合数学相关知识即可解决。由于结构不同的子树再怎么弄都不会成为本质相同的,所以我们将结构相同的子树分成一块。

    如果有两个重心就分别DP,最后合并的时候讨论一下就行了。

    时间复杂度$O(n)$。

    代码

    #include<bits/stdc++.h>
    #define rg register
    #define il inline
    #define co const
    template<class T>il T read()
    {
    	rg T data=0;
    	rg int w=1;
    	rg char ch=getchar();
    	while(!isdigit(ch))
    	{
    		if(ch=='-') w=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))
    	{
    		data=data*10+ch-'0';
    		ch=getchar();
    	}
    	return data*w;
    }
    template<class T>il T read(rg T&x)
    {
    	return x=read<T>();
    }
    typedef unsigned long long ll;
    using namespace std;
    
    co int N=5e5+1,P=233,mod=1e9+7;
    ll p[N];
    int n,inv[N];
    int add(int x,int y)
    {
    	x+=y;
    	return x>=mod?x-mod:x;
    }
    int mul(int x,int y)
    {
    	return (ll)x*y%mod;
    }
    int calc(int n,int m)
    {
    	int ans=1;
    	for(int i=m;i;--i)
    		ans=mul(ans,mul(inv[i],n+1-i));
    	return ans;
    }
    
    vector<int>g[N];
    int siz[N],G1,G2;
    void get_root(int x,int fa)
    {
    	siz[x]=1;
    	bool flag=1;
    	for(int i=0;i<g[x].size();++i)
    	{
    		int y=g[x][i];
    		if(y==fa) continue;
    		get_root(y,x);
    		siz[x]+=siz[y];
    		if(siz[y]*2>n) flag=0;
    	}
    	if(siz[x]*2<n) flag=0;
    	if(flag) !G1?G1=x:G2=x;
    }
    
    int q[N],top,dp[N][2];
    ll hs[N];
    bool cmp(co int&a,co int&b)
    {
    	return hs[a]<hs[b];
    }
    void dfs(int x,int fa)
    {
    	hs[x]=1,siz[x]=1;
    	for(int i=0;i<g[x].size();++i)
    	{
    		int y=g[x][i];
    		if(y==fa) continue;
    		dfs(y,x);
    		siz[x]+=siz[y],hs[x]+=p[siz[y]]*hs[y];
    	}
    	top=0; // edit 1: enqueue later because the use of q
    	for(int i=0;i<g[x].size();++i)
    	{
    		int y=g[x][i];
    		if(y==fa) continue;
    		q[++top]=y;
    	}
    	sort(q+1,q+top+1,cmp);
    	dp[x][0]=dp[x][1]=1;
    	for(int i=1,j;i<=top;i=j)
    	{
    		for(j=i+1;j<=top&&hs[q[i]]==hs[q[j]];++j);
    		int v=q[i];
    		dp[x][0]=mul(dp[x][0],calc(add(dp[v][0],add(dp[v][1],j-i-1)),j-i));
    		dp[x][1]=mul(dp[x][1],calc(add(dp[v][0],j-i-1),j-i));
    	}
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	read(n);
    	p[0]=inv[0]=inv[1]=1,p[1]=P;
    	for(int i=2;i<=n;++i)
    		inv[i]=mul(inv[mod%i],mod-mod/i),p[i]=p[i-1]*P;
    	for(int i=1;i<n;++i)
    	{
    		int x=read<int>(),y=read<int>();
    		g[x].push_back(y),g[y].push_back(x);
    	}
    	get_root(1,1);
    	G2?(dfs(G1,G2),dfs(G2,G1)):dfs(G1,G1);
    	int ans;
    	if(G2)
    	{
    		if(hs[G1]!=hs[G2])
    			ans=add(mul(dp[G1][0],dp[G2][1]),add(mul(dp[G1][1],dp[G2][0]),mul(dp[G1][0],dp[G2][0])));
    		else
    			ans=add(mul(dp[G1][0],dp[G1][1]),calc(add(dp[G1][0],1),2))%mod;
    	}
    	else
    		ans=add(dp[G1][0],dp[G1][1]);
    	printf("%d
    ",ans);
    	return 0;
    }
    /*
    5
    1 2
    1 3
    1 4
    1 5
    */
    
  • 相关阅读:
    webpack
    npm
    关于js click事件、touch事件的 screen 、client
    同源策略、jsonp、阻塞事件
    关于height、width、top
    新建空白图片
    配置环境
    异常02
    异常01
    集合框架08
  • 原文地址:https://www.cnblogs.com/autoint/p/10330452.html
Copyright © 2011-2022 走看看