题意

写的很明白了,不需要解释。

\(\texttt{Data Range:}1\leq n\leq 234567\)

题解

国 际 计 数 水 平

首先考虑一开始只有一个黑点的情况怎么做。

我们钦定黑点为根,设 \(f_i\) 表示以 \(i\) 为子树,并且 \(i\) 这个节点为黑色的答案,那么我们得到:

\[f_u=(sz_u-1)!\prod\limits_{fa_v=u}\frac{f_v}{sz_v!}
\]

接下来我们阐述这个式子到底是在干什么。

首先我们枚举可能的排列,由于现在只有 \(u\) 是黑色的,第一轮肯定是先把 \(u\) 变成红色。也就是说,\(u\) 只能放在第一个,而剩下的可以任意。(注意这里是可能而不是合法)

接下来考虑排除掉所有不合法的排列,枚举每一棵子树,考虑某棵子树所有节点之间的偏序关系。

对于一个树 \(T\) 对应的可能的序列 \(P\) 来说,我们按顺序从 \(P\) 中选出一些元素构造 \(Q\),使得 \(Q\) 中的每一个元素都是 \(T\) 中某个子树 \(S\) 中存在的节点的编号,并且 \(S\) 中每个节点的编号都要在 \(Q\) 中出现。

这里,\(T\) 的根为 \(u\),\(S\) 的根为 \(v\) 且 \(fa_v=u\)。

然后我们发现,如果序列 \(Q\) 是不合法的,那么序列 \(P\) 也是不合法的。

对所有子树都这么做就得到同时满足所有约束的序列个数,也就是上面的式子。

接下来我们考虑直接计算根节点的答案。

\[f_{rt}=\frac{(sz_{rt}-1)!}{\prod\limits_{fa_v=rt}sz_v!}\prod\limits_{fa_v=rt}f_v
\]

然后我们对于所有根节点的儿子 \(v\) 代入进来,再将所有孙子代入进来……相当于我们枚举所有节点计算,于是有

\[f_{rt}=\prod\limits_{u=1}^{n}\frac{(sz_{u}-1)!}{\prod\limits_{fa_v=u}sz_v!}
\]

简单化开

\[f_{rt}=\frac{\prod\limits_{u=1}^{n}(sz_{u}-1)!}{\prod\limits_{u=1}^{n}\prod\limits_{fa_v=u}sz_v!}
\]

注意分母,也就是说我们对于每个节点 \(u\) 都枚举 \(u\) 的所有儿子,相当于枚举所有非根节点(因为每个非根节点总会是某个节点的儿子),于是有

\[f_{rt}=\frac{\prod\limits_{u=1}^{n}(sz_{u}-1)!}{\prod\limits_{u=1,u\neq rt}^{n}sz_u!}
\]

现在来拆分子。单独把 \(u=rt\) 的拿出来,剩下的合并并约分,得到

\[f_{rt}=(sz_{rt}-1)!\prod\limits_{u=1,u\neq rt}^{n}\frac{1}{sz_u}
\]

然后分子分母同乘一个 \(sz_{rt}\) 得到答案为

\[f_{rt}=sz_{rt}!\prod\limits_{u=1}^{n}\frac{1}{sz_u}
\]

接下来考虑两个黑点咋做,建立一个虚拟节点 \(s\),向 \(a,b\) 连无向边,初始的时候只有 \(s\) 是黑点。

第一次只能变 \(s\),然后就变成 \(s\) 是红点,\(a,b\) 是黑点。容易证明这样对答案没有任何影响。

于是树就变成了一颗基环树,考虑怎么求答案。

我们枚举环上染红的最后一个点的位置,但是这是不可行的。

那么我们就用类似于 NOIp2018 Day 2 T1 的方法,考虑枚举一条在环上的边并删除他来计算答案。

但是这样做可能会用重复的答案,所以我们要考虑如何来排除这些答案。

注意到对于一种染色方案来说,考虑这个方案在环上最后一个被染成红色的点。我们发现这个点可以有两种方案被染黑,也就是说每个方案都会算 \(2\) 此,于是最终答案要乘上 \(\frac{1}{2}\)。

对于不是环上的点和虚拟节点 \(s\) 我们都能用 \(\texttt{dfs}\) 求出子树大小,设他们的和为 \(z\)。

我们现在考虑这些在环上的点,对于某个在环上的点 \(u\) 来说,设 \(a_u\) 表示它的确定子树大小(也就是所有不在环上的儿子的大小之和加一),\(b_u\) 表示某种断环方式下的 \(sz_u\),那么通过人类智慧我们发现

将环上的 \(k+1\) 个点编号,\(s\) 点为 \(0\),其余的依次为 \(1\sim k\),假设断的边是 \(r\to r+1\),那么

\[b_u=\begin{cases}\sum\limits_{i=1}^{u}a_u,& 1\leq u\leq r\\\sum\limits_{i=u}^{k}a_u,&r+1\leq u\leq k\end{cases}
\]

我们对 \(a\) 做前缀和变成 \(c\),那么可以发现

\[\prod b=\prod_{i\neq j}\vert c_i-c_j\vert
\]

其中 \(c_0=0\)。

然后我们可以用快速插值的套路去优化这个东西,复杂度 \(O(n\log^2n)\)。

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#define clr(f,n) memset(f,0,sizeof(int)*(n))
#define cpy(f,g,n) memcpy(f,g,sizeof(int)*(n))
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
using namespace std;
typedef int ll;
typedef long long int li;
typedef unsigned long long int ull;
const ll MAXN=524291,MOD=998244353,G=3,INVG=332748118;
struct Edge{
ll to,prev;
};
Edge ed[MAXN];
ll n,x,y,z,fct,from,to,tot,tp,cur,res;
ll rev[MAXN],omgs[MAXN],invo[MAXN];
ll last[MAXN],sz[MAXN],fa[MAXN],st[MAXN],p[MAXN],c[MAXN];
ll pr[MAXN],px[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
inline void addEdge(ll from,ll to)
{
ed[++tot].prev=last[from];
ed[tot].to=to;
last[from]=tot;
}
inline void dfs(ll node,ll f)
{
sz[node]=1,fa[node]=f;
for(register int i=last[node];i;i=ed[i].prev)
{
if(ed[i].to!=f)
{
dfs(ed[i].to,node),sz[node]+=sz[ed[i].to];
}
}
}
inline ll qpow(ll base,ll exponent)
{
ll res=1;
while(exponent)
{
if(exponent&1)
{
res=(li)res*base%MOD;
}
base=(li)base*base%MOD,exponent>>=1;
}
return res;
}
inline void setupOmg(ll cnt)
{
ll limit=log2(cnt)-1,omg;
omg=qpow(G,(MOD-1)>>(limit+1)),omgs[cnt>>1]=1;
for(register int i=(cnt>>1|1);i!=cnt;i++)
{
omgs[i]=(li)omgs[i-1]*omg%MOD;
}
for(register int i=(cnt>>1)-1;i;i--)
{
omgs[i]=omgs[i<<1];
}
}
inline void NTT(ll *cp,ll cnt,ll inv)
{
static ull tcp[MAXN];
register ll cur=0,x,shift=log2(cnt)-__builtin_ctz(cnt);
if(inv==-1)
{
reverse(cp+1,cp+cnt);
}
for(register int i=0;i<cnt;i++)
{
tcp[rev[i]>>shift]=cp[i];
}
for(register int i=2;i<=cnt;i<<=1)
{
cur=i>>1;
for(register int j=0;j<cnt;j+=i)
{
for(register int k=0;k<cur;k++)
{
x=tcp[j|k|cur]*omgs[k|cur]%MOD;
tcp[j|k|cur]=tcp[j|k]+MOD-x,tcp[j|k]+=x;
}
}
}
for(register int i=0;i<cnt;i++)
{
cp[i]=tcp[i]%MOD;
}
if(inv==1)
{
return;
}
x=MOD-(MOD-1)/cnt;
for(register int i=0;i<cnt;i++)
{
cp[i]=(li)cp[i]*x%MOD;
}
}
inline void conv(ll fd,ll *f,ll *g,ll *res)
{
static ll tmpf[MAXN],tmpg[MAXN];
ll cnt=1,limit=-1;
while(cnt<(fd<<1))
{
cnt<<=1,limit++;
}
for(register int i=0;i<cnt;i++)
{
tmpf[i]=i<fd?f[i]:0,tmpg[i]=i<fd?g[i]:0;
rev[i]=(rev[i>>1]>>1)|((i&1)<<limit);
}
NTT(tmpf,cnt,1),NTT(tmpg,cnt,1);
for(register int i=0;i<cnt;i++)
{
tmpf[i]=(li)tmpf[i]*tmpg[i]%MOD;
}
NTT(tmpf,cnt,-1),cpy(res,tmpf,fd);
}
inline void inv(ll fd,ll *f,ll *res)
{
static ll tmp[MAXN];
if(fd==1)
{
res[0]=qpow(f[0],MOD-2);
return;
}
inv((fd+1)>>1,f,res);
ll cnt=1,limit=-1;
while(cnt<(fd<<1))
{
cnt<<=1,limit++;
}
for(register int i=0;i<cnt;i++)
{
tmp[i]=i<fd?f[i]:0;
rev[i]=(rev[i>>1]>>1)|((i&1)<<limit);
}
NTT(tmp,cnt,1),NTT(res,cnt,1);
for(register int i=0;i<cnt;i++)
{
res[i]=(li)(2-(li)tmp[i]*res[i]%MOD+MOD)%MOD*res[i]%MOD;
}
NTT(res,cnt,-1),clr(res+fd,cnt-fd-1);
}
inline void mod(ll fd,ll gd,ll *f,ll *g,ll *r)
{
static ll tmpf[MAXN],tmpg[MAXN],tinv[MAXN],q[MAXN];
if(fd<gd)
{
for(register int i=0;i<gd-1;i++)
{
r[i]=f[i];
}
return;
}
for(register int i=0;i<fd;i++)
{
tmpf[i]=f[fd-1-i];
}
for(register int i=0;i<gd;i++)
{
tmpg[i]=g[gd-1-i];
}
inv(fd-gd+2,tmpg,tinv);
ll cnt=1,limit=-1;
while(cnt<(fd<<1))
{
cnt<<=1,limit++;
}
for(register int i=0;i<cnt;i++)
{
rev[i]=(rev[i>>1]>>1)|((i&1)<<limit);
}
NTT(tmpf,cnt,1),NTT(tinv,cnt,1);
for(register int i=0;i<cnt;i++)
{
q[i]=1ll*tmpf[i]*tinv[i]%MOD;
}
NTT(q,cnt,-1),reverse(q,q+fd-gd+1);
for(register int i=0;i<cnt;i++)
{
tmpf[i]=tinv[i]=tmpg[i]=0;
q[i]=i<fd-gd+1?q[i]:0,g[i]=i<gd?g[i]:0;
}
cnt>>=1,limit--;
for(register int i=0;i<cnt;i++)
{
rev[i]=(rev[i>>1]>>1)|((i&1)<<limit);
}
NTT(q,cnt,1),NTT(g,cnt,1);
for(register int i=0;i<cnt;i++)
{
tmpf[i]=1ll*q[i]*g[i]%MOD;
}
NTT(g,cnt,-1),NTT(tmpf,cnt,-1);
for(register int i=0;i<gd-1;i++)
{
r[i]=(f[i]-tmpf[i]+MOD)%MOD;
}
for(register int i=0;i<cnt;i++)
{
q[i]=tmpf[i]=0;
}
}
vector<ll> tmpf2[MAXN<<2];
void dnc(ll *pts,ll l,ll r,ll node)
{
static ll tmp[MAXN],tmp2[MAXN];
if(l==r)
{
tmpf2[node].push_back((MOD-pts[l])%MOD),tmpf2[node].push_back(1);
return;
}
ll mid=(l+r)>>1,ls=node<<1,rs=ls|1;
dnc(pts,l,mid,ls),dnc(pts,mid+1,r,rs);
ll d=tmpf2[ls].size(),d2=tmpf2[rs].size();
copy(tmpf2[ls].begin(),tmpf2[ls].end(),tmp);
copy(tmpf2[rs].begin(),tmpf2[rs].end(),tmp2);
ll cnt=1,limit=-1;
while(cnt<(d+d2))
{
cnt<<=1,limit++;
}
for(register int i=0;i<cnt;i++)
{
rev[i]=(rev[i>>1]>>1)|((i&1)<<limit);
}
NTT(tmp,cnt,1),NTT(tmp2,cnt,1);
for(register int i=0;i<cnt;i++)
{
tmp[i]=(li)tmp[i]*tmp2[i]%MOD;
}
NTT(tmp,cnt,-1),tmpf2[node].resize(d+d2-1);
copy(tmp,tmp+d+d2-1,tmpf2[node].begin()),clr(tmp,cnt),clr(tmp2,cnt);
}
ll tmpf3[19][MAXN];
void dnc2(ll fd,ll *f,ll depth,ll l,ll r,ll node,ll *res)
{
static ll tmp[MAXN],pw[17];
if(r-l<=1024)
{
for(register int i=l;i<=r;i++)
{
ll x=c[i],cur=f[fd-1],v1,v2,v3,v4;
pw[0]=1;
for(register int j=1;j<=16;j++)
{
pw[j]=1ll*pw[j-1]*x%MOD;
}
for(register int j=fd-2;j-15>=0;j-=16)
{
v1=(1ll*cur*pw[16]+1ll*f[j]*pw[15]+
1ll*f[j-1]*pw[14]+1ll*f[j-2]*pw[13])%MOD;
v2=(1ll*f[j-3]*pw[12]+1ll*f[j-4]*pw[11]+
1ll*f[j-5]*pw[10]+1ll*f[j-6]*pw[9])%MOD;
v3=(1ll*f[j-7]*pw[8]+1ll*f[j-8]*pw[7]+
1ll*f[j-9]*pw[6]+1ll*f[j-10]*pw[5])%MOD;
v4=(1ll*f[j-11]*pw[4]+1ll*f[j-12]*pw[3]+
1ll*f[j-13]*pw[2]+1ll*f[j-14]*pw[1])%MOD;
cur=(0ll+v1+v2+v3+v4+f[j-15])%MOD;
}
for(register int j=((fd-1)&15)-1;~j;j--)
{
cur=(1ll*cur*x+f[j])%MOD;
}
res[i]=cur;
}
return;
}
ll sz=tmpf2[node].size()-1;
for(register int i=0;i<sz+1;i++)
{
tmp[i]=tmpf2[node][i];
}
clr(tmpf3[depth],sz+10),mod(fd,sz+1,f,tmp,tmpf3[depth]);
ll mid=(l+r)>>1;
dnc2(sz,tmpf3[depth],depth+1,l,mid,node<<1,res);
dnc2(sz,tmpf3[depth],depth+1,mid+1,r,(node<<1)|1,res);
for(register int i=0;i<sz;i++)
{
tmpf3[depth][i]=0;
}
}
inline void eval(ll fd,ll pcnt,ll *f,ll *pts,ll *res)
{
dnc(pts,0,pcnt-1,1),dnc2(fd,f,0,0,pcnt-1,1,res);
}
int main()
{
setupOmg(524288),n=read(),x=read(),y=read();
for(register int i=0;i<n-1;i++)
{
from=read(),to=read();
addEdge(from,to),addEdge(to,from);
}
dfs(x,0);
while(y!=x)
{
st[++tp]=y,p[y]=1,y=fa[y];
}
st[++tp]=y,p[y]=1,y=fa[y],z=n+1,fct=1;
for(register int i=1;i<=tp;i++)
{
c[i]=1;
for(register int j=last[st[i]];j;j=ed[j].prev)
{
if(!p[ed[j].to])
{
c[i]+=sz[ed[j].to];
}
}
}
for(register int i=1;i<=n;i++)
{
fct=(li)fct*i%MOD;
if(!p[i])
{
z=(li)z*sz[i]%MOD;
}
}
fct=(li)fct*(n+1)%MOD;
for(register int i=1;i<=tp;i++)
{
c[i]=(c[i-1]+c[i])%MOD;
}
dnc(c,1,tp+1,1);
for(register int i=1;i<=tp+1;i++)
{
pr[i-1]=(li)i*tmpf2[1][i]%MOD;
}
memset(tmpf2,0,sizeof(tmpf2)),eval(n+1,tp+1,pr,c,px);
for(register int i=0;i<=tp;i++)
{
cur=(li)fct*qpow((li)px[i]*z%MOD,MOD-2)%MOD;
res=(res+(((tp-i)&1)?MOD-cur:cur))%MOD;
}
printf("%d\n",(li)res*499122177%MOD);
}

Luogu P5450 [THUPC2018]淘米神的树的更多相关文章

  1. 【LOJ】#6391. 「THUPC2018」淘米神的树 / Tommy

    题解 一道非常神仙的计数题 如果只有一个点,就是非常简单的树型dp \(f_{u} = (siz_{u} - 1)! \prod_{v \in son_{u}} \frac{f_{v}}{siz_{v ...

  2. Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分)

    Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分) Description L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之 ...

  3. [Luogu 3701] 「伪模板」主席树

    [Luogu 3701] 「伪模板」主席树 这是一道网络流,不是主席树,不是什么数据结构,而是网络流. 题目背景及描述都非常的暴力,以至于 Capella 在做此题的过程中不禁感到生命流逝. S 向 ...

  4. [题解] Luogu P5446 [THUPC2018]绿绿和串串

    [题解] Luogu P5446 [THUPC2018]绿绿和串串 ·题目大意 定义一个翻转操作\(f(S_n)\),表示对于一个字符串\(S_n\), 有\(f(S)= \{S_1,S_2,..., ...

  5. 上午小测3 T1 括号序列 && luogu P5658 [CSP/S 2019 D1T2] 括号树 题解

    前 言: 一直很想写这道括号树..毕竟是在去年折磨了我4个小时的题.... 上午小测3 T1 括号序列 前言: 原来这题是个dp啊...这几天出了好几道dp,我都没看出来,我竟然折磨菜. 考试的时候先 ...

  6. 【kAri OJ】621. 廖神的树

    时间限制 3000 ms 内存限制 65536 KB 题目描述 廖神和男神在植树节的时候准备玩一个奇怪的游戏,他们现在有一个被分割成n*n个格子的矩形土地,他们现在准备往这个地里种树,但这个种树游戏必 ...

  7. Luogu Dynamic Ranking (带修改的主席树)

    题目大意: 网址:https://www.luogu.org/problemnew/show/2617 给定一个序列a[1].a[2].....a[N],完成M个操作,操作有两种: [1]Q i j ...

  8. Luogu 4294 [WC2008]游览计划 | 斯坦纳树

    题目链接 Luogu 4294 (我做这道题的时候BZOJ全站的SPJ都炸了 提交秒WA 幸好有洛谷) 题解 这道题是[斯坦纳树]的经典例题.斯坦纳树是这样一类问题:带边权无向图上有几个(一般约10个 ...

  9. Luogu 3369 / BZOJ 3224 - 普通平衡树 - [替罪羊树]

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 https://www.luogu.org/problemnew/show/P3 ...

随机推荐

  1. python实现随机复制若干个文件到新目录

    python实现随机复制若干个文件到新目录 1说明 1.1 目的 随机选择一个文件下的若干个文件,并将文件复制到新文件夹下 1.2 要求 需要将random_select_and_copy_file. ...

  2. 极简 Node.js 入门 - 4.3 可读流

    极简 Node.js 入门系列教程:https://www.yuque.com/sunluyong/node 本文更佳阅读体验:https://www.yuque.com/sunluyong/node ...

  3. linux(centos)环境下安装rabbitMq

    1.由于rabbitMq是用Erlang语言写的,因此要先安装Erlang环境 下载Erlang :http://www.rabbitmq.com/releases/erlang/erlang-19. ...

  4. 003 01 Android 零基础入门 01 Java基础语法 01 Java初识 03 Java程序的执行流程

    003 01 Android 零基础入门 01 Java基础语法 01 Java初识 03 Java程序的执行流程 Java程序长啥样? 首先编写一个Java程序 记事本编写程序 打开记事本 1.wi ...

  5. Python3基础——字符串类型

    Text Sequence Type - str(immutable) class str(object='') class str(object=b'', encoding='utf-8', err ...

  6. 固件(Firmware)

    来源:https://baike.baidu.com/item/%E5%9B%BA%E4%BB%B6/627829 固件   固件(Firmware)就是写入EPROM(可擦写可编程只读存储器)或EE ...

  7. P3419 [POI2005]SAM-Toy Cars / SP688 SAM - Toy Cars

    一道很妙的贪心题 题面 我们考虑当我们插入时会面临的两种情况 当地上的玩具,不满 \(k\) 个时,那我们直接放就可以了. 当满了 \(k\) 个的时候,我们就要从地上拿出一个来给当前的腾位置. 这就 ...

  8. 腾讯云服务器,使用xshell ssh秘钥登录的时候报错:所选的用户密钥未在远程主机上注册

    1.背景 新买了台腾讯云服务器,在腾讯云控制台新建了SSH私钥,然后下载到本地. 在本地使用xshell工具,导入下载好的私钥进行登录,报错:所选的用户密钥未在远程主机上注册 2.解决方案 在确定选择 ...

  9. ORA-00018: maximum number of sessions exceeded 超出最大会话数

    ORA-00018: maximum number of sessions exceededORA-00018: 超出最大会话数 Cause:       All session state obje ...

  10. centos7 yum 安装nodejs、npm、cnpm、pm2、yarn

    一.环境准备 1.1 查看系统环境 [root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) [ ...