[HNOI2016]树

2 篇文章 0 订阅
2 篇文章 0 订阅

题意:给出一颗树和每一条边出现的概率和 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<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<sstream>
#include<climits>
#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<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
#define zqf 998244353
using namespace std;
template<typename T>void 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;N<size;N<<=1,STEP++);
        memset(rev,0,sizeof(rev));
        for(int i=0;i<N;i++)
            for(int j=STEP;j>=0;j--)
                if((1<<j)&i)
                    rev[i]|=(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;i<N;i++)
            if(i<rev[i])
                swap(A[i],A[rev[i]]);
        for(int k=1;k<N;k<<=1)
            for(int i=0;i<N;i+=(k<<1))
                for(int j=0;j<k;j++)
                {
                    LL x=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<N;i++)
                A[i]=(A[i]*inv)%zqf;
    }
    void mul(LL A[],LL B[],LL C[])
    {
        ntt(A,1);
        ntt(B,1);
        for(int i=0;i<N;i++)
            C[i]=A[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<n;i++)
    {
        LL a,b,u,v;
        Read(u),Read(v),Read(a),Read(b);
        add(u,v,a*ksm(b,zqf-2)%zqf);
    }
    DFS(1,-1,0);
    cout<<ans<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值