P10912 [蓝桥杯 2024 国 B] 数星星 题解

Sol

题意转化为给定一棵树,有多少点集 \(G\) 满足 \(L \le |V_G| \le R\) 且它是一颗菊花树。

我们考虑对于每一个节点,算出以其为根的菊花树造成了多少贡献。

最暴力的方法实际上就是先枚举点集内点的个数 \(x\),那么贡献即为 \(\sum\limits_{i=1}^n C_{du_i}^{x-1}\)

于是我们就有一个 \(O(n^2)\) 的暴力解法,答案为:

\[\sum_{x=L}^{R}\sum\limits_{i=1}^n C_{du_i}^{x-1} \]

考虑怎么优化,显然你可以把两个求和倒过来:

\[\sum\limits_{i=1}^n\sum_{x=L}^{R} C_{du_i}^{x-1} \]

然后你发现相同的两个 \(du_i\) 后面的一堆算出来的答案是相同的,于是我们记忆化一下。然后你发现你过了。

因为一棵树不同的度数最多只有 \(\lceil \sqrt{n} \rceil\) 个,然后总时间复杂度是 \(O(n \sqrt{n})\) 的。

Hack

你会发现如果按照上面那样算,在 \(x=2\) 的时候每条边的 \(2\) 个端点会各算一次只有这两个点的集合,这样就重复了。

然后就有了下面这组 Hack:

2
1 2
2 2

Hack 掉了 luogu 里面 \(2\) 篇题解!

所以特判一下 \(L\le 2\) 就好了,数据太水了。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long 

inline int read()
{
    int x(0);
    short f(1);
    char ch(getchar());
    while(!isdigit(ch))f=(ch=='-')?-1:1,ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}

int qpow(int x,int y,int p)
{
    if(y==0)
        return 1;
    int tmp=qpow(x,y>>1,p);
    if(y&1)
        return tmp*tmp%p*x%p;
    return tmp*tmp%p;
}

const int N=2e5+5,mod=1e9+7;
int n,du[N],num[N];

namespace plzh
{

int fac[N],inv[N];

inline void init()
{
    fac[0]=inv[0]=1;
    for(int i=1;i<=n;i++)
        fac[i]=fac[i-1]*i%mod;
    inv[n]=qpow(fac[n],mod-2,mod);
    for(int i=n-1;i>=1;i--)
        inv[i]=inv[i+1]*(i+1)%mod;
    return ;
}

inline int C(int m,int n)
{
    if(m<n)
        return 0;
    return fac[m]*inv[n]%mod*inv[m-n]%mod;
}}

signed main()
{
    n=read();
    for(int i=1;i<=n-1;i++)
    {
        int x(read()),y(read());
        du[x]++;
        du[y]++;
    }
    for(int i=1;i<=n;i++)
        num[i]=-1;
    plzh::init();
    int L(read()),R(read()),ans=0;
    if(L==1)
        ans+=n,L++;
    if(L==2&&L<=R)
        ans+=n-1,L++;
    for(int i=1;i<=n;i++)
    {
        if(num[du[i]]!=-1)
        {
            ans=(ans+num[du[i]])%mod;
            continue;
        }
        int now=0;
        for(int j=L-1;j<=R-1;j++)
            now=(now+plzh::C(du[i],j))%mod;
        ans=(ans+now)%mod;
        num[du[i]]=now;
    }
    cout<<ans;
    return 0;
}
posted @ 2025-03-11 20:16  tmp_get_zip_diff  阅读(99)  评论(0)    收藏  举报