bzoj3162 独钓寒江雪

149 篇文章 0 订阅
25 篇文章 0 订阅

Description

这里写图片描述

这里写图片描述

Input

Output

Sample Input

Sample Output

HINT

Source

2013湖北互测week1
http://www.elijahqi.win/archives/2923

orz vfk orz icefox

想做这道题很久了 看网上题解也看了很久了始终明白不了 后来有幸看到icefox巨佬的题解看到了http://vfleaking.blog.163.com/blog/static/17480763420134452440444/

去看一看vfk原来的题解就会豁然开朗

如何hash一棵树 学习了下scarlyw巨佬的blog 就是根据子树大小把子树的hash值都加起来即可 如果专门只求树上独立集计数的话 答案就是设dp[i][0/1]分别表示当前处于i号节点我是否选取当前节点 那么dp[i][1]=显然就是所有子树中不选的乘积

dp[i][0]就是所有子树中选与不选和的乘积

学习了一个组合数学的相关知识 当时bj集训的时候有一道题我看出来是这样做了然而不会算这个的通式 即:有n种物品要求从中选m个求问方案数 那么就是c(n+m-1)(m)

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

#include<cstdio>
#include<cctype>
#include<algorithm>
#define g 233
#define N 500010
using namespace std;
const int mod=1e9+7;
#define ll unsigned long long
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S) {T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
struct node{
    int y,next;
}data[N<<1];
ll p[N],hs[N];ll dp[N][2];//1->choose this node 0->not choose
int size[N],h[N],n,G1,G2,inv[N],num; 
inline void get_root(int x,int fa){
    size[x]=1;bool flag=1;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (y==fa) continue;
        get_root(y,x);size[x]+=size[y];
        if (size[y]*2>n) flag=0;
    }if (size[x]*2<n) flag=0;if (flag) !G1?G1=x:G2=x;
}
inline bool cmp(const int &a,const int &b){return hs[a]<hs[b];}
inline int calc(ll n,int m){static ll ans;ans=1;n%=mod;
    for (int i=m;i;--i) ans=ans*inv[i]%mod*(n+1-i)%mod;return ans;
}
inline void dfs(int x,int fa){
    static int q[N],top;hs[x]=1;size[x]=1;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if(y==fa) continue;
        dfs(y,x);size[x]+=size[y];hs[x]+=p[size[y]]*hs[y];
    }top=0;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;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){static int v;
        for (j=i+1;j<=top&&hs[q[i]]==hs[q[j]];++j);v=q[i];
        dp[x][0]=dp[x][0]*calc(dp[v][0]+dp[v][1]+j-i-1,j-i)%mod;
        dp[x][1]=dp[x][1]*calc(dp[v][0]+j-i-1,j-i)%mod;
    }
}
int main(){
    freopen("bzoj3162.in","r",stdin);
    n=read();p[0]=1,p[1]=g,inv[1]=1;
    for (int i=2;i<=n;++i) inv[i]=(ll)inv[mod%i]*(mod-mod/i)%mod,p[i]=p[i-1]*g;
    for (int i=1;i<n;++i){
        static int x,y;x=read();y=read();
        data[++num].y=y;data[num].next=h[x];h[x]=num;
        data[++num].y=x;data[num].next=h[y];h[y]=num;
    }get_root(1,1);int ans=0;
    G2?(dfs(G1,G2),dfs(G2,G1)):dfs(G1,G1);
    if(G2){
        if (hs[G1]!=hs[G2]) ans=(dp[G1][0]*dp[G2][1]+dp[G1][1]*dp[G2][0]+dp[G1][0]*dp[G2][0])%mod;
        else ans=(dp[G1][0]*dp[G1][1]+calc(dp[G1][0]+1,2))%mod;
    }else ans=(dp[G1][0]+dp[G1][1])%mod;
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值