WC2019 T1 数树

WC2019 T1 数树

传送门(https://loj.ac/problem/2983

Question 0

对于给定的两棵树,设记两颗树 \(A,B\) 的重边为 \(R(A,B)\),那么
\[
Ans=y^{n-R(A,B)}
\]

Question 1

给定其中一棵树,求第二棵树的所有情况下答案的总和

不妨令 \(y=y^{-1}\) ,最终答案就是 \(y^{-n}y^{R(A,B)}\)。

在给定 \(A\) 的情况下,只需要统计 \(\sum\limits_B y^{R(A,B)}\) 即可

注意到\(y^k=[(y-1)+1]^k=\sum\limits_{i=0}^k (y-1)^i \binom{k}{i}\)

及对于确定的 \(A,B\),枚举一个边集 \(S\) ,若 \(S\) 中每一条边均为 \(A,B\) 重边,则其贡献为 \((y-1)^{|S|}\) 否则为 \(0\)

特别的,我们把 \(y-1=0?\) 特判掉,因为不存在逆元。

考虑枚举每一个 \(A?\) 的边集,假设一共 \(n-m?\) 条边把 \(n?\) 个点划分成了 \(m?\) 个联通块。

设第 \(i?\) 个联通块有 \(a_i?\) 个点那么它的贡献为 \((y-1)^{n-m}\times?\) 包含这\(n-m?\) 条边的树的个数。

把每一个联通块当作一个点,考虑枚举每个联通块的度数 \(d_i\) 通过 \(prufer\) 序列我们知道答案为
\[
Ans=(y-1)^{n-m}\sum\limits_{(\sum a_i)=n,(\sum d_i)=2(m-2)} m^{m-2}(\prod_{i=1}^m a_i^{d_i})(\prod_{i=1}^m \binom{\sum_{j=1}^i(d_j-1)}{d_i-1}) \=(y-1)^{n-m}\sum\limits_{(\sum a_i)=n,(\sum d_i)=2m-2} m^{m-2}(\prod_{i=1}^m a_i^{d_i})\frac{(m-2)!}{\prod_{i=1}^m(d_i-1)!}\=(y-1)^{n-m}\sum\limits_{(\sum a_i)=n,(\sum d_i)=m-2} (\prod_{i=1}^m a_i)(m-2)!m^{m-2}\prod_{i=1}^m \frac{a_i^{d_i}}{d_i!}\=(y-1)^{n-m}\sum\limits_{(\sum a_i)=n} (\prod_{i=1}^m a_i)n^{m-2}\=(y-1)^n n^{-2}\sum\limits_{(\sum a_i)=n} (\prod_{i=1}^m a_i)n^m (y-1)^{-m}\=(y-1)^n n^{-2}\sum\limits_{(\sum a_i)=n} (\prod_{i=1}^m a_i(y-1)^{-1}n)\\]
注意 \((\prod_{i=1}^m a_i)\) 表示每个联通块中选一个关键点的方案数量,这样就可以设 \(F_{x,0/1}\) 表示 \(x\) 号点所在联通块是否选出关键点的贡献和,树形 \(DP\) ,每次考虑父子的边是否被选中即可。

Question 2

和 \(Question\ 1?\) 的做法类似,先特判 $y-1=?$0 考虑枚举一个边集构成了 $ m ?$ 个的联通块的答案
\[
Ans=(y-1)^{n-m}\sum\limits_{(\sum a_i)=n}\frac{n!}{\prod_{i=1}^m a_i}\prod a_i^{a_i-2}\frac {1}{m!}(n^{m-2}\prod_{i=1}^m a_i)^2\=(y-1)^n n^{-4} n!\sum\limits_{(\sum a_i)=n}\frac {1}{m!}{\prod_{i=1}^m \frac{a_i^{a_i}}{n^2 a_i!(y-1)}}\\]
之需要考虑 \(F_n=\sum\limits_{(\sum a_i)=n}\frac {1}{m!}{\prod_{i=1}^m \frac{a_i^{a_i}}{n^2 a_i!(y-1)}}?\) 的结果即可。

若设大小为 $k $ 的部分的贡献为 \(G(k)=\frac{k^k}{n^2 k! (y-1)}\),考虑 \(e^x\) 即 \(exp(x)\) 的泰勒展开式为 \(\sum_{i=0}^{+INF} \frac{x^i}{i!}\) 。

有 \(F=exp(G)\) ,那么多项式求 \(exp\) 即可。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
int read(){
    int nm=0,fh=1; char cw=getchar();
    for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    return nm*fh;
}
#define pii pair<int,int>
#define mp make_pair
#define mod 998244353
#define M 600010
namespace  CALC{
    inline int add(int x,int y){x+=y;return (x>=mod)?(x-mod):x;}
    inline int mns(int x,int y){x-=y;return (x<0)?(x+mod):x;}
    inline int mul(LL x,LL y){return (LL)x*(LL)y%mod;}
    inline void upd(int &x,int y){x+=y;(x>=mod)?(x-=mod):0;}
    inline int qpow(int x,int sq){
        int res=1;
        for(;sq;sq>>=1,x=mul(x,x)) if(sq&1) res=mul(res,x);
        return res;
    }
}using namespace CALC;
namespace POLY{
    int lg[M],g[40],v[40],od[M],iv[40],vv[M];
    void init(int N){
        N<<=2; int len=2,nw=1;
        for(;len<=N;len<<=1,nw++)
            lg[len]=nw,v[nw]=qpow(g[nw]=qpow(3,(mod-1)/len),mod-2),iv[nw]=qpow(len,mod-2);
        for(int i=1;i<=len;i++) vv[i]=qpow(i,mod-2);vv[0]=0;
    }
    void NTT(int *x,int len,int kd){
        int bas=lg[len];
        for(int i=1;i<len;i++) od[i]=(od[i>>1]>>1)|((i&1)<<(bas-1));
        for(int i=1;i<len;i++) if(i<od[i]) swap(x[i],x[od[i]]);
        for(int tt=1,tp=1;tt<len;tp++,tt<<=1){
            for(int wn=(kd>0)?g[tp]:v[tp],st=0;st<len;st+=(tt<<1)){
                for(int now=1,pos=st;pos<st+tt;pos++,now=mul(now,wn)){
                    int t1=x[pos],t2=mul(now,x[pos+tt]);
                    x[pos]=add(t1,t2),x[pos+tt]=mns(t1,t2);
                }
            }
        } if(kd>0) return;
        for(int i=0;i<len;i++) x[i]=mul(x[i],iv[bas]);
    }
    inline void cpy(int *_dt,int *_ss,int len){memcpy(_dt,_ss,sizeof(int)*len);}
    void get_inv(int *F,int *G,int len){
        static int A[M],B[M];
        if(len==1){G[0]=qpow(F[0],mod-2);return;} get_inv(F,G,len>>1);
        cpy(A,F,len),cpy(B,G,len),len<<=1,NTT(A,len,1),NTT(B,len,1);
        for(int i=0;i<len;i++) G[i]=mns(add(B[i],B[i]),mul(mul(B[i],B[i]),A[i]));
        for(int i=0;i<len;i++) A[i]=B[i]=0; NTT(G,len,-1),len>>=1;
        for(int i=len;i<len+len;i++) G[i]=0;
    }
    inline void qd(int *F,int len){for(int i=1;i<len;i++) F[i-1]=mul(F[i],i); F[len-1]=0;}
    inline void jf(int *F,int len){for(int i=len-1;i;i--) F[i]=mul(F[i-1],vv[i]); F[0]=0;}
    void get_ln(int *F,int *G,int len){
        static int A[M],B[M];
        get_inv(F,A,len),cpy(B,F,len),qd(B,len),len<<=1;
        NTT(A,len,1),NTT(B,len,1);
        for(int i=0;i<len;i++) G[i]=mul(A[i],B[i]),A[i]=B[i]=0;
        NTT(G,len,-1),jf(G,len),len>>=1;
        for(int i=len;i<(len<<1);i++) G[i]=0;
    }
    void get_exp(int *F,int *G,int len){
        static int A[M],B[M];
        if(len==1){G[0]=1;return;} get_exp(F,G,len>>1);
        cpy(A,G,len),get_ln(G,B,len);
        for(int i=0;i<len;i++) B[i]=mns(F[i],B[i]);
        len<<=1,NTT(A,len,1),NTT(B,len,1);
        for(int i=0;i<len;i++) G[i]=mul(A[i],B[i]),A[i]=B[i]=0;
        NTT(G,len,-1),len>>=1;
        for(int i=len;i<len+len;i++) G[i]=0;
    }
}
int n,Y,op,ans,nts;
map<pii,bool> P;
namespace Q1{
    int fs[M],nt[M],to[M],tmp,F[M][2],K,vK,G[2];
    inline void link(int x,int y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y;}
    inline void DP(int x,int last){
        F[x][0]=F[x][1]=K;
        for(int i=fs[x],v;i!=-1;i=nt[i]) if((v=to[i])^last){
            DP(to[i],x),G[0]=0,G[1]=0;
            for(int j=0;j<2;j++) for(int k=0;k<2;k++){
                if(!(j&k)) upd(G[j|k],mul(mul(F[x][j],F[v][k]),vK));
                if(k) upd(G[j],mul(F[x][j],F[v][k]));
            } F[x][0]=G[0],F[x][1]=G[1];
        }
    }
    inline int solve(){
        memset(fs,-1,sizeof(fs));
        for(int i=1,x,y;i<n;i++) x=read(),y=read(),link(x,y),link(y,x);
        if(Y==1) return qpow(n,n-2); K=mul(qpow(Y-1,mod-2),n),vK=qpow(K,mod-2),DP(1,0);
        return mul(mul(F[1][1],nts),mul(qpow(mul(n,n),mod-2),qpow(Y-1,n)));
    }
}
namespace Q2{
    int F[M],G[M];
    inline int solve(){
        if(Y==1) return qpow(n,n+n-4); int len=1,fc=1; POLY::init(n),F[0]=1;
        for(int i=1;i<=n;i++)
            F[i]=mul(mul(mul(n,n),mul(qpow(i,i),qpow(fc=mul(fc,i),mod-2))),qpow(Y-1,mod-2));
        while(len<=n) len<<=1; POLY::get_exp(F,G,len);
        return mul(mul(mul(G[n],fc),mul(nts,qpow(n,mod-1-4))),qpow(Y-1,n));
    }
}
int main(){
    //freopen("tree.in","r",stdin);
    //freopen("tree.out","w",stdout);
    n=read(),Y=read(),op=read(),nts=qpow(Y,n),Y=qpow(Y,mod-2);
    if(op==0){
        for(int i=1,x,y;i<n;i++) x=read(),y=read(),P[mp(x,y)]=P[mp(y,x)]=true;
        for(int i=1;i<n;i++) if(P.count(mp(read(),read()))) nts=mul(nts,Y);
        printf("%d\n",nts); return 0;
    }
    if(op&1) printf("%d\n",Q1::solve());
    else printf("%d\n",Q2::solve());
    return 0;
}

原文地址:https://www.cnblogs.com/OYJason/p/10427135.html

时间: 2024-10-15 06:25:37

WC2019 T1 数树的相关文章

【loj2983】【WC2019】数树

题目 两颗\(n\)个点的树T1和T2,有\(y\)种颜色; 现在给每个点染色,要求公共边端点的颜色相同,求: ? 1.op=0 , T1和T2都确定,求合法染色方案数: ? 2.op=1 , T1确定,求所有T2的合法染色方案数的和 ; ? 3.op=2 , 求所有(T1,T2)合法染色方案数的和: \(mod \ 998244353\)的值: $1 \le n \le 10^5 ?, ?1 \le y \lt 998244353 ?, ?op = { 0,1,2 } $ ; 题解 op=0

【WC2019】数树 树形DP 多项式exp

题目大意 有两棵 \(n\) 个点的树 \(T_1\) 和 \(T_2\). 你要给每个点一个权值吗,要求每个点的权值为 \([1,y]\) 内的整数. 对于一条同时出现在两棵树上的边,这条边的两个端点的值相同. 若 \(op=0\),则给你两棵树 \(T_1,T_2\),求方案数. 若 \(op=1\),则给你一棵树 \(T_1\),求对于所有 \(n^{n-2}\) 种 \(T_2\),方案数之和. 若 \(op=2\),则求对于所有的 \(T_1,T_2\),求方案数之和. \(n\leq

Thair数 树状数组

题目: Erwin最近对一种叫"thair"的东西巨感兴趣... 在含有n个整数的序列a1,a2......an中, 三个数被称作"thair"当且仅当i<j<k且ai<aj<ak 求一个序列中"thair"的个数. Input (thair.in) 开始一个正整数n, 以后n个数a1~an. Output (thair.out) "thair"的个数 Sample Input Output 4 2 1

1006数树11

小黄山上死掉的树木,上次已经被全部更换上了新的树苗,经过半年左右已经开始茁壮成长.2015级的小伙伴想对小黄山上的数目进行清点,在一个矩形区域内进行数树.横着数有m棵,竖着数有n棵,问总共有多少棵? 输入有多组测试用例.对于每组测试用例,包含两个正整数,分别为m和n(0 < m, n <= 1000). 对于每组测试用例输出一个正整数,矩形区域内总共有多少棵树. 2 6 3 5 12 15 #include"stdio.h"#include"math.h"

[WC2019] 数树

Statement 有\(n\)个节点, 分别用红线,蓝线连成两棵树. 用\(y\)种颜色给节点染色, 规定如果一条边在两棵树中同时出现, 那么边两端的点的颜色必须相同. Task #1: 给定两棵树, 求染色方案. Task #2: 给定其中一棵树, 求对于另一棵树的每一种形态的染色方案数之和. Task #3: 两棵树的形态都没有确定, 求对于所有情况的染色方案数之和. (\(n\le 10^5\)) Solution Task #1 计算有多少条公共边即可. Task #2 如果有\(i\

hdu 4911 求逆序对数+树状数组

http://acm.hdu.edu.cn/showproblem.php?pid=4911 给定一个序列,有k次机会交换相邻两个位置的数,问说最后序列的逆序对数最少为多少. 实际上每交换一次能且只能减少一个逆序对,所以问题转换成如何求逆序对数. 归并排序或者树状数组都可搞 树状数组: 先按大小排序后分别标号,然后就变成了求1~n的序列的逆序数,每个分别查询出比他小的用i减,在把他的值插入即可 #include <cstdio> #include <cstdlib> #includ

求序列中满足Ai &lt; Aj &gt; Ak and i &lt; j &lt; k的组数 树状数组 HIT 2275 Number sequence

http://acm.hit.edu.cn/hoj/problem/view?id=2275 Number sequence   Source : SCU Programming Contest 2006 Final   Time limit : 1 sec   Memory limit : 64 M Submitted : 1632, Accepted : 440 Given a number sequence which has N element(s), please calculate

Hihocoder #1014 : Trie树 (字典数树统计前缀的出现次数 *【模板】 基于指针结构体实现 )

#1014 : Trie树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能对于每一个我给出的字符串,都在这个词典里面找到以这个字符串开头的所有单词呢?” 身经百战的小Ho答道:“怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的所有单词,检查你给我的字

Achen毒瘤模拟题T1——数数(counting)

题目大意:给出一个区间,并给定应两两相等的数位的数对,求满足的数的个数. 范围:1e5 做法: 先用并查集,将互相相等的树连成一块,统计块的个数,从高位到低位扫描,然后容斥即可 #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<set> #include<queue> #inc