题解

一道非常神仙的计数题

如果只有一个点,就是非常简单的树型dp

\(f_{u} = (siz_{u} - 1)! \prod_{v \in son_{u}} \frac{f_{v}}{siz_{v}!}\)

\(\frac{f_{u}}{siz_{u}!} = \frac{1}{siz_{u}} \prod_{v \in son_{u}} \frac{f_{v}}{siz_{v}!}\)

\(f_{u} = \frac{n!}{\prod s_{i}}\)

可是我们有两个点,我们把这两个点连起来的点作为第一个黑点的话,这就是一个环套树,把环拎出来,我们枚举在哪里断开

我们设环长为m,添加上的黑点即\(v_0\)同时\(v_0\)和\(v_m\)相连

这样的话,如果我们要让\(v_{i}\)是最后一个被染红的,那么不在环上的点的子树大小都不会改变

我们断开\(v_{i} - v_{i + 1}\)和\(v_{i - 1} - v_{i}\)都可以

也就是说,我们断开一条边,对应着两个点被最后一个染色的方案数

总共计算了两遍,我们最后的答案除二就行

现在,我们把问题转化成了,枚举断开的一条边,把图变成一棵树,求这棵树的方案数,即套用我们所求的公式,需要的就是快速算出环上的点的子树大小更新的情况

我们用\(s_{i}\)表示第i个点上所挂的树的大小,并把这个东西处理成一个前缀和

我们对于每个点,它的值是

\(\prod_{i \neq j } (s_{i} - s_{j})\)

我们要求的是

\(\sum_{i} \prod_{i \neq q} (s_{i} - s_{j})\)

我们如果把\(s_{i}\)设成变量,那么

\(\sum_{i} \prod_{i \neq q} (x- s_{j})\)这个式子可以联系到导数

也就是

\(\sum_{i} \prod_{i \neq q} (x - s_{j}) = \frac{\mathrm{d} }{\mathrm{d} x} \prod (x - s_{i})\)

然后我们直接多项式插值就可以了

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <queue>
#include <ctime>
#include <map>
#include <set>
#define fi first
#define se second
#define pii pair<int,int>
//#define ivorysi
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define MAXN 234600
using namespace std;
typedef long long int64;
typedef double db;
typedef unsigned int u32;
template<class T>
void read(T &res) {
res = 0;T f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9' ) {
res = res * 10 - '0' + c;
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
} const int MOD = 998244353;
int W[(1 << 20) + 5];
int mul(int a,int b) {
return 1LL * a * b % MOD;
}
int inc(int a,int b) {
return a + b >= MOD ? a + b - MOD : a + b;
}
int fpow(int x,int c) {
int res = 1,t = x;
while(c) {
if(c & 1) res = mul(res,t);
t = mul(t,t);
c >>= 1;
}
return res;
}
struct node {
int to,next;
}E[MAXN * 2];
int head[MAXN],sumE,N,A,B,siz[MAXN],fa[MAXN],sum[MAXN],tot,fac,inv[MAXN];
int ans[MAXN];
void add(int u,int v) {
E[++sumE].to = v;
E[sumE].next = head[u];
head[u] = sumE;
}
void dfs(int u) {
siz[u] = 1;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(v != fa[u]) {
fa[v] = u;
dfs(v);
siz[u] += siz[v];
}
}
}
struct poly {
vector<int> p;
poly() {
p.clear();
}
void print() {
for(int i = 0 ; i < p.size() ; ++i) {out(p[i]);space;}
enter;
}
friend void NTT(poly &f,int L,int on) {
f.p.resize(L);
for(int i = 1, j = L / 2 ; i < L - 1 ; ++i) {
if(i < j) swap(f.p[i],f.p[j]);
int k = L / 2;
while(j >= k) {
j -= k;
k >>= 1;
}
j += k;
}
for(int h = 2 ; h <= L ; h <<= 1) {
int wn = W[((1 << 20) + on * (1 << 20) / h) % (1 << 20)];
for(int k = 0 ; k < L ; k += h) {
int w = 1;
for(int j = k ; j < k + h / 2 ; ++j) {
int u = f.p[j],t = mul(w,f.p[j + h / 2]);
f.p[j] = inc(u,t);
f.p[j + h / 2] = inc(u,MOD - t);
w = mul(w,wn);
}
}
}
if(on == -1) {
int InvL = fpow(L,MOD - 2);
for(int i = 0 ; i < L ; ++i) {
f.p[i] = mul(f.p[i],InvL);
}
}
}
friend poly operator * (poly f,poly g) {
int L = f.p.size() + g.p.size();
int t = 1;
while(t <= L) t <<= 1;
poly h;h.p.resize(t);
NTT(f,t,1);NTT(g,t,1);
for(int i = 0 ; i < t ; ++i) {
h.p[i] = mul(f.p[i],g.p[i]);
}
NTT(h,t,-1);
for(int i = t - 1 ; i >= 0; --i) {
if(h.p[i] == 0) h.p.pop_back();
else break;
}
return h;
}
friend poly operator + (poly f,poly g) {
poly h;
int t = max(f.p.size(),g.p.size());
f.p.resize(t);g.p.resize(t);
for(int i = 0 ; i < t ; ++i) {
h.p.pb(inc(f.p[i],g.p[i]));
}
for(int i = t - 1; i >= 0 ; --i) {
if(!h.p[i]) h.p.pop_back();
else break;
}
return h;
}
friend poly operator - (poly f,poly g) {
poly h;
int t = max(f.p.size(),g.p.size());
f.p.resize(t);g.p.resize(t);
for(int i = 0 ; i < t ; ++i) {
h.p.pb(inc(f.p[i],MOD - g.p[i]));
}
for(int i = t - 1; i >= 0 ; --i) {
if(!h.p[i]) h.p.pop_back();
else break;
}
return h;
}
friend poly Inverse(poly f,int L) {
poly g,r,two;
two.p.pb(2);
g.p.pb(fpow(f.p[0],MOD - 2));r.p.pb(f.p[0]);
int t = 1;
while(t <= L) {
int m = min(t * 2,(int)f.p.size());
for(int i = t ; i < m ; ++i) {
r.p.pb(f.p[i]);
}
t = t * 2;
g = g * (two - r * g);
int tmp = g.p.size();
for(int i = tmp - 1; i >= t ; --i) g.p.pop_back();
}
t = g.p.size();
for(int i = t - 1 ; i >= L ; --i) g.p.pop_back();
t = L - 1;
for(int i = t ; i >= 0 ; --i) {
if(!g.p[i]) g.p.pop_back();
else break;
}
return g;
}
friend poly operator / (poly f,poly g) {
reverse(f.p.begin(),f.p.end());
reverse(g.p.begin(),g.p.end());
int t = f.p.size() - g.p.size() + 1;
poly h = Inverse(g,t);
poly q = f * h;
for(int i = q.p.size() - 1 ; i >= t ; --i) q.p.pop_back();
reverse(q.p.begin(),q.p.end());
for(int i = t - 1 ; i >= 0 ; --i) {
if(!q.p[i]) q.p.pop_back();
else break;
} return q;
}
friend poly operator % (poly f,poly g) {
return f - g * (f / g);
}
};
poly tr[MAXN * 4];
poly Solve1(int u,int l,int r) {
if(l == r) {
tr[u].p.pb(MOD - sum[l]);tr[u].p.pb(1);
return tr[u];
}
int mid = (l + r) >> 1;
tr[u << 1] = Solve1(u << 1,l,mid);
tr[u << 1 | 1] = Solve1(u << 1 | 1,mid + 1,r);
return tr[u] = tr[u << 1] * tr[u << 1 | 1];
}
void Solve2(int u,int l,int r,poly f) {
if(l == r) {
if(!f.p.size()) ans[l] = 0;
else ans[l] = f.p[0];
return;
}
int mid = (l + r) >> 1;
Solve2(u << 1,l,mid,f % tr[u << 1]);
Solve2(u << 1 | 1,mid + 1,r,f % tr[u << 1 | 1]);
}
void Init() {
W[0] = 1;
W[1] = fpow(3,(MOD - 1) / (1 << 20));
for(int i = 2 ; i < (1 << 20) ; ++i) {
W[i] = mul(W[i - 1],W[1]);
}
read(N);read(A);read(B);
int x,y;
for(int i = 1 ; i < N ; ++i) {
read(x);read(y);
add(x,y);add(y,x);
}
dfs(A);
int f = B,t = 0;
fac = 1;
while(t != A) {
sum[++tot] = siz[f] - siz[t];
fac = mul(fac,siz[f]);
t = f;
f = fa[f];
}
for(int i = 1 ; i <= tot ; ++i) sum[i] += sum[i - 1];
inv[1] = 1;
for(int i = 2 ; i <= N; ++i) {
inv[i] = mul(inv[MOD % i],MOD - MOD / i);
fac = mul(fac,i);
}
for(int i = 1 ; i <= N ; ++i) fac = mul(fac,inv[siz[i]]); }
void Solve() {
poly f = Solve1(1,0,tot);
int t = f.p.size();
for(int i = 0 ; i < t - 1 ; ++i) {
f.p[i] = mul(f.p[i + 1],i + 1);
}
f.p.pop_back(); Solve2(1,0,tot,f);
int tmp = 1;
for(int i = tot ; i >= 0 ; --i) {
ans[i] = mul(ans[i],tmp);
tmp = mul(tmp,MOD - 1);
}
tmp = 0;
for(int i = 0 ; i <= tot ; ++i) {
tmp = inc(tmp,fpow(ans[i],MOD - 2));
}
fac = mul(fac,tmp);
fac = mul(fac,inv[2]);
out(fac);enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
Solve();
return 0;
}

【LOJ】#6391. 「THUPC2018」淘米神的树 / Tommy的更多相关文章

  1. loj 3090 「BJOI2019」勘破神机 - 数学

    题目传送门 传送门 题目大意 设$F_{n}$表示用$1\times 2$的骨牌填$2\times n$的网格的方案数,设$G_{n}$$表示用$1\times 2$的骨牌填$3\times n$的网 ...

  2. LOJ 3090 「BJOI2019」勘破神机——斯特林数+递推式求通项+扩域

    题目:https://loj.ac/problem/3090 题解:https://www.luogu.org/blog/rqy/solution-p5320 1.用斯特林数把下降幂化为普通的幂次求和 ...

  3. LOJ#6387 「THUPC2018」绿绿与串串 / String (Manacher || hash+二分)

    题目描述 绿绿和 Yazid 是好朋友.他们在一起做串串游戏. 我们定义翻转的操作:把一个串以最后一个字符作对称轴进行翻转复制.形式化地描述就是,如果他翻转的串为 RRR,那么他会将前 ∣R∣−1个字 ...

  4. Luogu P5450 [THUPC2018]淘米神的树

    题意 写的很明白了,不需要解释. \(\texttt{Data Range:}1\leq n\leq 234567\) 题解 国 际 计 数 水 平 首先考虑一开始只有一个黑点的情况怎么做. 我们钦定 ...

  5. LOJ 3055 「HNOI2019」JOJO—— kmp自动机+主席树

    题目:https://loj.ac/problem/3055 先写了暴力.本来想的是 n<=300 的那个在树上暴力维护好整个字符串, x=1 的那个用主席树维护好字符串和 nxt 数组.但 x ...

  6. LOJ 2339 「WC2018」通道——边分治+虚树

    题目:https://loj.ac/problem/2339 两棵树的话,可以用 CTSC2018 暴力写挂的方法,边分治+虚树.O(nlogn). 考虑怎么在这个方法上再加一棵树.发现很难弄. 看了 ...

  7. LOJ 2302 「NOI2017」整数——压位线段树

    题目:https://loj.ac/problem/2302 压30位,a最多落在两个位置上,拆成两次操作. 该位置加了 a 之后,如果要进位或者借位,查询一下连续一段 0 / 1 ,修改掉,再在含有 ...

  8. loj#2312. 「HAOI2017」八纵八横(线性基 线段树分治)

    题意 题目链接 Sol 线性基+线段树分治板子题.. 调起来有点自闭.. #include<bits/stdc++.h> #define fi first #define se secon ...

  9. 【LOJ】#3090. 「BJOI2019」勘破神机

    LOJ#3090. 「BJOI2019」勘破神机 为了这题我去学习了一下BM算法.. 很容易发现这2的地方是\(F_{1} = 1,F_{2} = 2\)的斐波那契数列 3的地方是\(G_{1} = ...

随机推荐

  1. day7 方法及基础知识运用

    做了一个小型的成绩管理系统.主要代码如下: /* * 功能:简易学生成绩管理系统 */package day7; import java.util.Scanner; public class Home ...

  2. RHEL-7.0重置root密码

    RHCE考试第一个环节就是重置root密码,然而7系列与6系列又存在着很大的不同.以下为RHEL-7.0系统对root密码重置的步骤! 1.开机出现引导菜单时按下e键    2.找到linux16行, ...

  3. Java并发编程原理与实战二十七:循环栅栏:CyclicBarrier

    昨天我们学习了倒计数功能的等待,今天我们学习的是循环栅栏:CyclicBarrier.下面我们就开始吧: 1.CyclicBarrier简介CyclicBarrier,是JDK1.5的java.uti ...

  4. 轻松使用div模拟select下拉菜单

    没有办法,平时不是万不得已我是不喜欢去模拟各类控件的,一个是麻烦,二个是对性能也有些影响,还是原生的来的实在.老板昨天发话,必须模拟赶紧的,老外最喜欢简洁干净的风格,说的貌似都很在理的样子,业务部也是 ...

  5. 超酷JQuery动画分页按钮,鼠标悬停滑动展开

    1.效果及功能说明 animate动画分页按钮制作鼠标悬停分页按钮上滑动展开分页按钮,鼠标离开后分页按钮收缩 2.实现原理 主要是靠动画方法,来让原本的箭头图像的长度发生变长,正好可以融入下标题的文字 ...

  6. 让vcmi支持英雄无敌3中文版

    Table of Contents 1 Hack 日志 2 Changes 3 Install by compiling 4 reply of Ivan 1 Hack 日志 8月22日开始动手修改改v ...

  7. HDU 1069 Monkey and Banana(最长递减子序列)

    题目链接 题意:摞长方体,给定长方体的长宽高,个数无限制,可随意翻转,要求下面的长方体的长和宽都大于上面的,都不能相等,问最多能摞多高. 题解:个数无限,其实每种形态最多就用一次,把每种形态都单独算一 ...

  8. AutoCAD DevTV-AUTOCAD二次开发资源合集

    Webcast Language Date AutoCAD .Net - Session 2 English 13-Sep-12 AutoCAD .Net - Session 1 English 6- ...

  9. Spring4笔记1--Spring概述、IoC

    Spring概述: Spring框架: Spring 由 20 多个模块组成,它们可以分为数据访问/集成(Data Access/Integration).Web.面向切面编程(AOP,  Aspec ...

  10. 13-6_mysql索引_1_Mysql_Learning_Notes_20180719_13-6

    mysql索引_1_Mysql_Learning_Notes 二分查找/折半查找法,binary search 一种在有序数组中查找某一特定元素的搜索算法; 二分查找法的优点是比较少次数,查找速度快, ...