[HNOI2016]树

题意:给出一颗树和每一条边出现的概率和 k ,和所有点的权值 a[i] ,定义一个联通块的特征值为 (a[i])k ,树的特征值为所有联通块特征值的和,求期望。
n,k2000
f[i][j] 表示以 i 为根的子树,指数为 j 时的树的特征值,有:
初始化 f[i][0]=1,f[i][j]=f[i][j1]×a[i]
合并子树 f[i][j]=(1p)f[i][j]+px=0jf[son][x]×f[i][jx]×Cxj
(1p)f[i][j]+pj!x=0jf[son][x]x!×f[i][jx](jx)!
NTT即可,这里给出模板程序。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define X first
#define Y second
#define DB double
#define lc now<<1
#define rc now<<1|1
#define MP make_pair
#define LL long long
#define pb push_back
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair
#define pdd pair
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
#define zqf 998244353
using namespace std;
templatevoid Read(T& x)
{
    x=0;int flag=0,sgn=1;char c;
    while(c=getchar())
    {
        if(c=='-')sgn=-1;
        else if(c>='0'&&c<='9')x*=10,x+=c-'0',flag=1;
        else if(flag)break;
    }
    x*=sgn;
}
const int MAXN=2100;
int first[MAXN],next[MAXN],to[MAXN],e,n,k;
LL w[MAXN],a[MAXN];
LL ksm(LL x,LL K)
{
    LL res=1,d=x;
    while(K)
    {
        if(K&1)
            res=(res*d)%zqf;
        d=(d*d)%zqf;
        K>>=1;
    }
    return res;
}
LL dp[MAXN][MAXN],aa[MAXN],b[MAXN],c[MAXN],ans=0,fac[MAXN],facinv[MAXN],lover[MAXN];
void add(int u,int v,LL l)
{
    ++e;next[e]=first[u];first[u]=e;to[e]=v;w[e]=l;
    ++e;next[e]=first[v];first[v]=e;to[e]=u;w[e]=l;
}
struct FFT
{
    int N,rev[MAXN],size,STEP,inv;
    LL ohm[MAXN];
    void init(int size)
    {
        for(N=1,STEP=0;N1,STEP++);
        memset(rev,0,sizeof(rev));
        for(int i=0;ifor(int j=STEP;j>=0;j--)
                if((1<1<<(STEP-j-1));//STEP-j-1!
        inv=ksm(N,zqf-2);
        ohm[0]=1;
        ohm[1]=ksm(3,(zqf-1)/N);
        for(int i=2;i<=N;i++)
            ohm[i]=ohm[i-1]*ohm[1]%zqf;
    }
    void ntt(LL A[],int flag)
    {
        for(int i=0;iif(ifor(int k=1;k1)
            for(int i=0;i1))
                for(int j=0;jx=A[i+j],
                       y=A[i+j+k]*(flag==1?ohm[N/(k<<1)*j]:ohm[N-N/(k<<1)*j])%zqf;
                    A[i+j]=(x+y)%zqf;
                    A[i+j+k]=(x-y+zqf)%zqf;
                }
        if(flag==-1)
            for(int i=0;i*inv)%zqf;
    }
    void mul(LL A[],LL B[],LL C[])
    {
        ntt(A,1);
        ntt(B,1);
        for(int i=0;i*B[i]%zqf;
        ntt(C,-1);
    }
}NTT;
void DFS(int u,int f,int fp)
{
    dp[u][0]=1;
    for(int i=1;i<=k;i++)
        dp[u][i]=(dp[u][i-1]*aa[u])%zqf;
    for(int i=first[u];i!=-1;i=next[i])
    {
        int v=to[i];
        if(v==f)continue;
        DFS(v,u,w[i]);
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(int j=0;j<=k;j++)
        {
            a[j]=facinv[j]*dp[v][j]%zqf;
            b[j]=facinv[j]*dp[u][j]%zqf;
        }
        NTT.mul(b,a,c);
        for(int j=0;j<=k;j++)
        {
            dp[u][j]=((1-w[i]+zqf)*dp[u][j]%zqf+(w[i]*fac[j]%zqf*c[j])%zqf)%zqf;
        }
    }
    ans=(ans+dp[u][k]*(1-fp+zqf)%zqf)%zqf;
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    memset(first,-1,sizeof(first));
    Read(n),Read(k);
    NTT.init(2*k+1);
    fac[0]=1;
    for(int i=1;i<=k;i++)
    {
        fac[i]=(fac[i-1]*i)%zqf;
        facinv[i]=ksm(fac[i],zqf-2);
    }
    facinv[0]=ksm(fac[0],zqf-2);
    for(int i=1;i<=n;i++)
        Read(aa[i]);
    for(int i=1;i*ksm(b,zqf-2)%zqf);
    }
    DFS(1,-1,0);
    cout<

你可能感兴趣的:(概率,树形dp,FFT)