题解

一道非常神仙的计数题

如果只有一个点,就是非常简单的树型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})\)

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

代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. #include <vector>
  6. #include <cmath>
  7. #include <queue>
  8. #include <ctime>
  9. #include <map>
  10. #include <set>
  11. #define fi first
  12. #define se second
  13. #define pii pair<int,int>
  14. //#define ivorysi
  15. #define mp make_pair
  16. #define pb push_back
  17. #define enter putchar('\n')
  18. #define space putchar(' ')
  19. #define MAXN 234600
  20. using namespace std;
  21. typedef long long int64;
  22. typedef double db;
  23. typedef unsigned int u32;
  24. template<class T>
  25. void read(T &res) {
  26. res = 0;T f = 1;char c = getchar();
  27. while(c < '0' || c > '9') {
  28. if(c == '-') f = -1;
  29. c = getchar();
  30. }
  31. while(c >= '0' && c <= '9' ) {
  32. res = res * 10 - '0' + c;
  33. c = getchar();
  34. }
  35. res *= f;
  36. }
  37. template<class T>
  38. void out(T x) {
  39. if(x < 0) {x = -x;putchar('-');}
  40. if(x >= 10) {
  41. out(x / 10);
  42. }
  43. putchar('0' + x % 10);
  44. }
  45. const int MOD = 998244353;
  46. int W[(1 << 20) + 5];
  47. int mul(int a,int b) {
  48. return 1LL * a * b % MOD;
  49. }
  50. int inc(int a,int b) {
  51. return a + b >= MOD ? a + b - MOD : a + b;
  52. }
  53. int fpow(int x,int c) {
  54. int res = 1,t = x;
  55. while(c) {
  56. if(c & 1) res = mul(res,t);
  57. t = mul(t,t);
  58. c >>= 1;
  59. }
  60. return res;
  61. }
  62. struct node {
  63. int to,next;
  64. }E[MAXN * 2];
  65. int head[MAXN],sumE,N,A,B,siz[MAXN],fa[MAXN],sum[MAXN],tot,fac,inv[MAXN];
  66. int ans[MAXN];
  67. void add(int u,int v) {
  68. E[++sumE].to = v;
  69. E[sumE].next = head[u];
  70. head[u] = sumE;
  71. }
  72. void dfs(int u) {
  73. siz[u] = 1;
  74. for(int i = head[u] ; i ; i = E[i].next) {
  75. int v = E[i].to;
  76. if(v != fa[u]) {
  77. fa[v] = u;
  78. dfs(v);
  79. siz[u] += siz[v];
  80. }
  81. }
  82. }
  83. struct poly {
  84. vector<int> p;
  85. poly() {
  86. p.clear();
  87. }
  88. void print() {
  89. for(int i = 0 ; i < p.size() ; ++i) {out(p[i]);space;}
  90. enter;
  91. }
  92. friend void NTT(poly &f,int L,int on) {
  93. f.p.resize(L);
  94. for(int i = 1, j = L / 2 ; i < L - 1 ; ++i) {
  95. if(i < j) swap(f.p[i],f.p[j]);
  96. int k = L / 2;
  97. while(j >= k) {
  98. j -= k;
  99. k >>= 1;
  100. }
  101. j += k;
  102. }
  103. for(int h = 2 ; h <= L ; h <<= 1) {
  104. int wn = W[((1 << 20) + on * (1 << 20) / h) % (1 << 20)];
  105. for(int k = 0 ; k < L ; k += h) {
  106. int w = 1;
  107. for(int j = k ; j < k + h / 2 ; ++j) {
  108. int u = f.p[j],t = mul(w,f.p[j + h / 2]);
  109. f.p[j] = inc(u,t);
  110. f.p[j + h / 2] = inc(u,MOD - t);
  111. w = mul(w,wn);
  112. }
  113. }
  114. }
  115. if(on == -1) {
  116. int InvL = fpow(L,MOD - 2);
  117. for(int i = 0 ; i < L ; ++i) {
  118. f.p[i] = mul(f.p[i],InvL);
  119. }
  120. }
  121. }
  122. friend poly operator * (poly f,poly g) {
  123. int L = f.p.size() + g.p.size();
  124. int t = 1;
  125. while(t <= L) t <<= 1;
  126. poly h;h.p.resize(t);
  127. NTT(f,t,1);NTT(g,t,1);
  128. for(int i = 0 ; i < t ; ++i) {
  129. h.p[i] = mul(f.p[i],g.p[i]);
  130. }
  131. NTT(h,t,-1);
  132. for(int i = t - 1 ; i >= 0; --i) {
  133. if(h.p[i] == 0) h.p.pop_back();
  134. else break;
  135. }
  136. return h;
  137. }
  138. friend poly operator + (poly f,poly g) {
  139. poly h;
  140. int t = max(f.p.size(),g.p.size());
  141. f.p.resize(t);g.p.resize(t);
  142. for(int i = 0 ; i < t ; ++i) {
  143. h.p.pb(inc(f.p[i],g.p[i]));
  144. }
  145. for(int i = t - 1; i >= 0 ; --i) {
  146. if(!h.p[i]) h.p.pop_back();
  147. else break;
  148. }
  149. return h;
  150. }
  151. friend poly operator - (poly f,poly g) {
  152. poly h;
  153. int t = max(f.p.size(),g.p.size());
  154. f.p.resize(t);g.p.resize(t);
  155. for(int i = 0 ; i < t ; ++i) {
  156. h.p.pb(inc(f.p[i],MOD - g.p[i]));
  157. }
  158. for(int i = t - 1; i >= 0 ; --i) {
  159. if(!h.p[i]) h.p.pop_back();
  160. else break;
  161. }
  162. return h;
  163. }
  164. friend poly Inverse(poly f,int L) {
  165. poly g,r,two;
  166. two.p.pb(2);
  167. g.p.pb(fpow(f.p[0],MOD - 2));r.p.pb(f.p[0]);
  168. int t = 1;
  169. while(t <= L) {
  170. int m = min(t * 2,(int)f.p.size());
  171. for(int i = t ; i < m ; ++i) {
  172. r.p.pb(f.p[i]);
  173. }
  174. t = t * 2;
  175. g = g * (two - r * g);
  176. int tmp = g.p.size();
  177. for(int i = tmp - 1; i >= t ; --i) g.p.pop_back();
  178. }
  179. t = g.p.size();
  180. for(int i = t - 1 ; i >= L ; --i) g.p.pop_back();
  181. t = L - 1;
  182. for(int i = t ; i >= 0 ; --i) {
  183. if(!g.p[i]) g.p.pop_back();
  184. else break;
  185. }
  186. return g;
  187. }
  188. friend poly operator / (poly f,poly g) {
  189. reverse(f.p.begin(),f.p.end());
  190. reverse(g.p.begin(),g.p.end());
  191. int t = f.p.size() - g.p.size() + 1;
  192. poly h = Inverse(g,t);
  193. poly q = f * h;
  194. for(int i = q.p.size() - 1 ; i >= t ; --i) q.p.pop_back();
  195. reverse(q.p.begin(),q.p.end());
  196. for(int i = t - 1 ; i >= 0 ; --i) {
  197. if(!q.p[i]) q.p.pop_back();
  198. else break;
  199. }
  200. return q;
  201. }
  202. friend poly operator % (poly f,poly g) {
  203. return f - g * (f / g);
  204. }
  205. };
  206. poly tr[MAXN * 4];
  207. poly Solve1(int u,int l,int r) {
  208. if(l == r) {
  209. tr[u].p.pb(MOD - sum[l]);tr[u].p.pb(1);
  210. return tr[u];
  211. }
  212. int mid = (l + r) >> 1;
  213. tr[u << 1] = Solve1(u << 1,l,mid);
  214. tr[u << 1 | 1] = Solve1(u << 1 | 1,mid + 1,r);
  215. return tr[u] = tr[u << 1] * tr[u << 1 | 1];
  216. }
  217. void Solve2(int u,int l,int r,poly f) {
  218. if(l == r) {
  219. if(!f.p.size()) ans[l] = 0;
  220. else ans[l] = f.p[0];
  221. return;
  222. }
  223. int mid = (l + r) >> 1;
  224. Solve2(u << 1,l,mid,f % tr[u << 1]);
  225. Solve2(u << 1 | 1,mid + 1,r,f % tr[u << 1 | 1]);
  226. }
  227. void Init() {
  228. W[0] = 1;
  229. W[1] = fpow(3,(MOD - 1) / (1 << 20));
  230. for(int i = 2 ; i < (1 << 20) ; ++i) {
  231. W[i] = mul(W[i - 1],W[1]);
  232. }
  233. read(N);read(A);read(B);
  234. int x,y;
  235. for(int i = 1 ; i < N ; ++i) {
  236. read(x);read(y);
  237. add(x,y);add(y,x);
  238. }
  239. dfs(A);
  240. int f = B,t = 0;
  241. fac = 1;
  242. while(t != A) {
  243. sum[++tot] = siz[f] - siz[t];
  244. fac = mul(fac,siz[f]);
  245. t = f;
  246. f = fa[f];
  247. }
  248. for(int i = 1 ; i <= tot ; ++i) sum[i] += sum[i - 1];
  249. inv[1] = 1;
  250. for(int i = 2 ; i <= N; ++i) {
  251. inv[i] = mul(inv[MOD % i],MOD - MOD / i);
  252. fac = mul(fac,i);
  253. }
  254. for(int i = 1 ; i <= N ; ++i) fac = mul(fac,inv[siz[i]]);
  255. }
  256. void Solve() {
  257. poly f = Solve1(1,0,tot);
  258. int t = f.p.size();
  259. for(int i = 0 ; i < t - 1 ; ++i) {
  260. f.p[i] = mul(f.p[i + 1],i + 1);
  261. }
  262. f.p.pop_back();
  263. Solve2(1,0,tot,f);
  264. int tmp = 1;
  265. for(int i = tot ; i >= 0 ; --i) {
  266. ans[i] = mul(ans[i],tmp);
  267. tmp = mul(tmp,MOD - 1);
  268. }
  269. tmp = 0;
  270. for(int i = 0 ; i <= tot ; ++i) {
  271. tmp = inc(tmp,fpow(ans[i],MOD - 2));
  272. }
  273. fac = mul(fac,tmp);
  274. fac = mul(fac,inv[2]);
  275. out(fac);enter;
  276. }
  277. int main() {
  278. #ifdef ivorysi
  279. freopen("f1.in","r",stdin);
  280. #endif
  281. Init();
  282. Solve();
  283. return 0;
  284. }

【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. Matlab修改背景颜色

    步骤: 1, 在matlab命令行中运行prefdir, 获取matlab.prf文件所在路径 2, 打开matlab.prf所在路径, 找到matlab.prf文件, 作备份 3, 将以下内容添加到 ...

  2. Spring Resource 类图

    插播个广告 老丈人家的粉皮儿,农产品,没有乱七八糟的添加剂,欢迎惠顾 

  3. Dubbo学习笔记2:Dubbo服务提供端与消费端应用的搭建

    Demo结构介绍 Demo使用Maven聚合功能,里面有三个模块,目录如下: 其中Consumer模块为服务消费者,里面TestConsumer和consumer.xml组成了基于Spring配置方式 ...

  4. Redis学习一:Nosql入门和概述

    现在Redis越来越火,为了适应技术的发展,开始学习一下Redis,在学习Redis之前先学习一下Nosql. 第一部分:入门概述 1.1 互联网时代背景下大机遇,为什么用nosql 1.1.1 单机 ...

  5. Java并发编程原理与实战八:产生线程安全性问题原因(javap字节码分析)

    前面我们说到多线程带来的风险,其中一个很重要的就是安全性,因为其重要性因此,放到本章来进行讲解,那么线程安全性问题产生的原因,我们这节将从底层字节码来进行分析. 一.问题引出 先看一段代码 packa ...

  6. 来自一个Backbone的Hello,World!

    MVC写这种程序真是够大材小用的了,可没想到居然这么抽象! // 这是一个管理者视图/控制/模型 的全局类 var App = { Models: {}, Views: {}, Controllers ...

  7. UCenter在JAVA项目中实现的单点登录应用实例

    Comsenz(康盛)的UCenter当前在国内的单点登录领域占据绝对份额,其完整的产品线令UCenter成为了账号集成方面事实上的标准. 基于UCenter,可以将Comsenz旗下的Discuz! ...

  8. 针对用户在个人中心绑定手机认证的一些js代码。

    需求: 1:手机号码校验(格式的校验,手机号码是否已经绑定过)---未实现 2:填完手机号码,点击发送验证码,手机会收到一条信息 3:发送验证码按钮不可用,变成重新发送的倒计时 1):60秒以后又可以 ...

  9. oracle常见错误对应代码与含义

    本篇文章是对oracle错误代码进行了详细的总结与分析,需要的朋友参考下 ORA-00001: 违反唯一约束条件 (.) ORA-00017: 请求会话以设置跟踪事件 ORA-00018: 超出最大会 ...

  10. sklearn进行拟合

    # codind:utf-8 from sklearn.linear_model import SGDRegressor,LinearRegression,Ridge from sklearn.pre ...