1. 程式人生 > >HDU 6267 Master of Random (找規律)

HDU 6267 Master of Random (找規律)

題意:

給你n(n<=1e5)個節點。第i個節點的父親是隨機的,範圍為0~i-1。

定義一棵樹的值為   \sum_{i=1}^{n}val[i] 其中val[i]為以i為根的子樹的所有點的權值之和。

給你a[i](i=1~n)(每個點的權值),求所有可能的樹的平均值。

思路:找規律,看每個節點在n確定時被計算了多少次,發現

n=1    1

n=2   1 2

n=3   2 4 5

n=4   6 12 15 17

n=5   224 48 60 68 74

發現每一個n 的最後一項等於n*sum[n-1]+(n-1)!,sum[0]=1。(這裡n從0開始)

然後每個n,第一項就是(n-1)!。即d[1]=(n-1)!

把差寫出來,就很容易發現n確定時,d[i]=d[i-1]+c[n-1]/(i-1);  而d[n]就是(n-1)*sum[n-2]+(n-2)!

然後預處理d[n],階乘,逆元就可以O(n)求答案了

程式碼:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=200010;
const ll mo=998244353;
ll n,m,k;
ll a[maxn],c[maxn],sum[maxn],d[maxn];
ll ans,ct,cnt,tmp,flag;
char s[maxn];
ll power(ll a,ll n)   //aµÄn´Î·½mod
{
    ll ans=1;
    a=a%mo;
    while (n)
    {
        if(n&1) ans=(ans*a)%mo;
        n>>=1;
        a=(a*a)%mo;
        }
    return ans;
}
int main()
{
    c[0]=1;
    for(int i=1;i<=100010;i++)
    {
        c[i]=(c[i-1]*(ll)i)%mo;
    }
    sum[0]=1;
    for(int i=1;i<=100010;i++)
    {
        sum[i]=(sum[i-1]*(ll)i%mo+c[i-1])%mo;
        //cout<<sum[i]<<endl;
    }
    int T,cas=1;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;   flag=1;
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)
        {
           scanf("%lld",&a[i]);
        }
        ll ans=0;
        d[1]=c[n-1];
        d[2]=d[1]*2;
        d[n]=sum[n-1];
        for(ll i=3;i<=n-1;i++)
        {
            d[i]=(d[i-1]+c[n-1]*power(i-1,mo-2)%mo)%mo;
            //cout<<d[i]<<' * '<<endl;
        }
        //cout<<ans<<endl;
        for(int i=1;i<=n;i++)
        {
            ans=(ans+a[i]*d[i]%mo)%mo;
        }
        ans=ans*power(c[n],mo-2)%mo;
        printf("%lld\n",ans);

    }
    return 0;
}