LOJ2983. 「WC2019」数树

task0

有\(i\)条边一样答案就是\(y^{n - i}\)

task1

这里有个避免容斥的方法,如果有\(i\)条边重复我们要算的是\(y^{n - i}\),设\(a = y^{-1}\)那么我们可以对于选了i条边的方案算\(a^{i}\)

可是这样需要容斥,所以有个神奇的技巧

\((a - 1 + 1)^{i} = \sum_{j = 0}^{i}(a - 1)^{j}\binom{i}{j}\)

这样,对于至少选了\(j\)条边的方案,每选一条边乘上一个\((a - 1)\)就可以避免容斥了(以下设\(z = a - 1\))

分成\(m\)个联通块方案数是

\(n^{m - 2}\prod_{i = 1}^{m}a_{i}\)

这相当于断开一条边乘一下N,且每个联通块要选出一个点,dp完了之后答案再乘上\(N^{-1}\)

就直接设\(dp[i][0/1]\)表示\(i\)所在的联通块是否选了一个点的方案数

task2

还是上面那个技巧,然后要爆推式子,假如硬点\(i\)条边被选了的有\(f_{i}\)棵树

\(\sum_{i = 1}^{N - 1}f_iz^{i}\)

\[f_{i} = \frac{N!}{\prod_{k = 1}^{M}a_{k}!} \frac{1}{(N - x)!} \prod_{i = k}^{M}a_{k}^{a_{k} - 2} (n^{n - x - 2} \prod_{k = 1}^{M}a_{k})^{2}
\]

从左到右分别是

给每个联通块分配点,消除联通块的顺序,联通块内生成树的个数,联通块形成的树的个数

有点乱,给合一下就是

\[f_{i} = N! \prod_{k = 1}^{M}\frac{a_{k}^{a_{k}}}{a_{k}!} \frac{n^{2(n - x - 2)}}{(N - x)!}
\]

然后我们把答案带进去

\[ans = N!\sum_{i = 1}^{N} \frac{Z^{N - i}n^{2i - 4}}{i!}\prod_{k = 1}^{M}\frac{a_{k}^{a_{k}}}{a_{k}!}
\]

我们给\(a_{k}\)设个生成函数,去掉和i无关的项就是

\[ans = N!\frac{Z^{N}}{n^{4}}\sum_{i = 1}^{N}\frac{\frac{n^{2i}}{Z^{i}}\prod_{k = 1}^{M}\frac{a_{k}^{a_{k}}x^{a_{k}}}{a_{k}!} }{i!}
\]

因为\(M = i\)

所以也可以写成

\[ans = N!\frac{Z^{N}}{n^{4}}\sum_{i = 1}^{N}\frac{[x^{n}](\frac{n^{2}}{Z}\frac{a_{k}^{a_{k}}x^{a_{k}}}{a_{k}!})^{i} }{i!}
\]

于是设\(F(x) = \sum_{i = 1}^{\infty} \frac{n^{2}}{Z}\frac{i^{i}}{i!}x^{i}\)

很容易知道上面后半部分的式子是\(e^{F(x)}\),然后乘上前面的系数就做完了

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
#define eps 1e-10
#define MAXN 100005
#define ba 47
//#define ivorysi
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef double db;
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 +c - '0';
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,MAXL = (1 << 20);
int N,Y,op,W[MAXL + 5];
int inc(int a,int b) {
return a + b >= MOD ? a + b - MOD : a + b;
}
int mul(int a,int b) {
return 1LL * a * b % MOD;
}
void update(int &x,int y) {
x = inc(x,y);
}
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;
}
namespace task0 {
bool vis[MAXN];
map<pii,int> zz;
void Main() {
int a,b;
for(int i = 1 ; i < N ; ++i) {
read(a);read(b);
if(a > b) swap(a,b);
zz[mp(a,b)] = 1;
}
int ans = fpow(Y,N);
int t = fpow(Y,MOD - 2);
for(int i = 1 ; i < N ; ++i) {
read(a);read(b);
if(a > b) swap(a,b);
if(zz[mp(a,b)]) ans = mul(ans,t);
}
out(ans);enter;
}
}
namespace task1 {
struct node {
int to,next;
}E[MAXN * 2];
int head[MAXN],sumE,Z;
int dp[MAXN][2],tmp[2];
void add(int u,int v) {
E[++sumE].to = v;
E[sumE].next = head[u];
head[u] = sumE;
}
void dfs(int u,int fa) {
dp[u][0] = 1,dp[u][1] = 1;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(v != fa) {
dfs(v,u);
tmp[0] = tmp[1] = 0;
update(tmp[1],mul(mul(dp[u][1],dp[v][1]),N));
update(tmp[1],mul(mul(dp[u][1],dp[v][0]),Z));
update(tmp[0],mul(mul(dp[u][0],dp[v][0]),Z));
update(tmp[0],mul(mul(dp[u][0],dp[v][1]),N));
update(tmp[1],mul(mul(dp[u][0],dp[v][1]),Z));
dp[u][0] = tmp[0];dp[u][1] = tmp[1];
}
}
}
void Main() {
int a,b;
for(int i = 1 ; i < N ; ++i) {read(a);read(b);add(a,b);add(b,a);}
Z = fpow(Y,MOD - 2);Z = inc(Z,MOD - 1);
dfs(1,0);
int ans = dp[1][1];
ans = mul(ans,fpow(N,MOD - 2));ans = mul(ans,fpow(Y,N));
out(ans);enter;
}
}
namespace task2 {
int fac[MAXN],invfac[MAXN],inv[MAXN];
vector<int> f,g;
void NTT(vector<int> &p,int L,int on) {
p.resize(L);
for(int i = 1,j = L >> 1; i < L - 1 ; ++i) {
if(i < j) swap(p[i],p[j]);
int k = L >> 1;
while(j >= k) {
j -= k;
k >>= 1;
}
j += k;
}
for(int h = 2 ; h <= L ; h <<= 1) {
int wn = W[(MAXL + on * MAXL / h) % MAXL];
for(int k = 0 ; k < L ; k += h) {
int w = 1;
for(int j = k ; j < k + h / 2 ; ++j) {
int u = p[j],t = mul(w,p[j + h / 2]);
p[j] = inc(u,t);
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) p[i] = mul(p[i],invL);
}
} void limit(vector<int> &p,int N) {
if(p.size() > N) p.resize(N);
}
vector<int> operator * (vector<int> a,vector<int> b) {
int t = a.size() + b.size() - 2;
int L = 1;
while(L <= t) L <<= 1;
NTT(a,L,1);NTT(b,L,1);
vector<int> c;c.resize(L);
for(int i = 0 ; i < L ; ++i) c[i] = mul(a[i],b[i]);
NTT(c,L,-1);
return c;
}
vector<int> Derivative(vector<int> p) {
vector<int> res(p.size() - 1);
for(int i = 0 ; i < p.size() - 1 ; ++i) {
res[i] = mul(p[i + 1],i + 1);
}
if(res.size() == 0) res.pb(0);
return res;
}
vector<int> Integral(vector<int> p) {
vector<int> res(p.size() + 1);
for(int i = 1 ; i <= p.size() ; ++i) {
res[i] = mul(p[i - 1],inv[i]);
}
return res;
}
vector<int> inverse(vector<int> p,int len) {
vector<int> g;
if(len == 1) {
g.pb(fpow(p[0],MOD - 2));
return g;
}
g = inverse(p,len >> 1);
int t = p.size() - 1 + 2 * (g.size() - 1);
int L = 1;
while(L <= t) L <<= 1;
NTT(p,L,1);NTT(g,L,1);
for(int i = 0 ; i < L ; ++i) {
g[i] = inc(mul(2,g[i]),MOD - mul(mul(g[i],g[i]),p[i]));
}
NTT(g,L,-1);
g.resize(len);
return g;
}
vector<int> Inverse(vector<int> p) {
int L = 1,t = p.size() - 1;
while(L <= t) L <<= 1;
return inverse(p,L);
}
vector<int> ln(vector<int> p) {
int t = p.size();
vector<int> g = Inverse(p) * Derivative(p);
g = Integral(g);limit(g,t);
return g;
}
vector<int> exp(vector<int> p,int len) {
vector<int> g,f;
if(len == 1) {
g.pb(1);return g;
}
g = exp(p,len >> 1);
p.resize(len);
f = g;f.resize(len);
f = ln(f);
int L = 1,t = len + g.size() - 1;
while(L <= t) L <<= 1;
NTT(p,L,1);NTT(f,L,1);NTT(g,L,1);
for(int i = 0 ; i < L ; ++i) {
g[i] = inc(g[i],mul(g[i],inc(p[i],MOD - f[i])));
}
NTT(g,L,-1);
limit(g,len);
return g;
}
vector<int> Exp(vector<int> p) {
int L = 1,t = p.size() - 1;
while(L <= t) L <<= 1;
return exp(p,L);
}
void Print(vector<int> c) {
for(int i = 0 ; i < c.size() ; ++i) {
out(c[i]);space;
}
enter;
}
void Main() {
if(Y == 1) {
out(fpow(N,2 * N - 4));enter;return;
}
int Z = fpow(Y,MOD - 2);Z = inc(Z,MOD - 1);
fac[0] = 1;
for(int i = 1 ; i <= N ; ++i) fac[i] = mul(fac[i - 1],i);
invfac[N] = fpow(fac[N],MOD - 2);
for(int i = N - 1 ; i >= 0 ; --i) invfac[i] = mul(invfac[i + 1],i + 1);
inv[1] = 1;
for(int i = 2 ; i <= N ; ++i) inv[i] = mul(inv[MOD % i],MOD - MOD / i);
int t = N,l = 1;
while(l <= t) l <<= 1;
f.resize(l);
int invZ = fpow(Z,MOD - 2);
for(int i = 1 ; i <= N ; ++i) {
f[i] = mul(N,N);
f[i] = mul(f[i],invZ);
f[i] = mul(f[i],fpow(i,i));f[i] = mul(f[i],invfac[i]);
}
W[0] = 1,W[1] = fpow(3,(MOD - 1) / MAXL);
for(int i = 2 ; i < MAXL ; ++i) {
W[i] = mul(W[i - 1],W[1]);
}
vector<int> a(3),b(3);
g = Exp(f);
int ans = g[N];
ans = mul(ans,fpow(Z,N));
ans = mul(ans,fpow(fpow(N,MOD - 2),4));
ans = mul(ans,fac[N]);
ans = mul(ans,fpow(Y,N));
out(ans);enter;
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
read(N);read(Y);read(op);
if(op == 0) task0::Main();
else if(op == 1) task1::Main();
else if(op == 2) task2::Main();
return 0;
}

【LOJ】#2983. 「WC2019」数树的更多相关文章

  1. LOJ#2983. 「WC2019」数树

    传送门 抄题解 \(Task0\),随便做一下,设 \(cnt\) 为相同的边的个数,输出 \(y^{n-cnt}\) \(Task1\),给定其中一棵树 设初始答案为 \(y^n\),首先可以发现, ...

  2. LOJ#2983. 「WC2019」数树 排列组合,生成函数,多项式,FFT

    原文链接www.cnblogs.com/zhouzhendong/p/LOJ2983.html 前言 我怎么什么都不会?贺忙指导博客才会做. 题解 我们分三个子问题考虑. 子问题0 将红蓝共有的边连接 ...

  3. Loj #2570. 「ZJOI2017」线段树

    Loj #2570. 「ZJOI2017」线段树 题目描述 线段树是九条可怜很喜欢的一个数据结构,它拥有着简单的结构.优秀的复杂度与强大的功能,因此可怜曾经花了很长时间研究线段树的一些性质. 最近可怜 ...

  4. [LibreOJ #2983]【WC2019】数树【计数】【DP】【多项式】

    Description 此题含有三个子问题 问题1: 给出n个点的两棵树,记m为只保留同时在两棵树中的边时连通块的个数,求\(y^m\) 问题2: 给出n个点的一棵树,另外一棵树任意生成,求所有方案总 ...

  5. loj#2269. 「SDOI2017」切树游戏

    还是loj的机子快啊... 普通的DP不难想到,设F[i][zt]为带上根玩出zt的方案数,G[i][zt]为子树中的方案数,后面是可以用FWT优化的 主要是复习了下动态DP #include< ...

  6. @loj - 2093@ 「ZJOI2016」线段树

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 小 Yuuka 遇到了一个题目:有一个序列 a1,a2,..., ...

  7. @loj - 3043@「ZJOI2019」线段树

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 九条可怜是一个喜欢数据结构的女孩子,在常见的数据结构中,可怜最喜 ...

  8. LOJ #2985. 「WC2019」I 君的商店

    传送门 搬题解QwQ 首先最大值一定为 \(1\),直接扫一遍两两比较 \(O(2N)\) 求出最大值 设最大值位置为 \(a\),对于任意两个没有确定的位置 \(x,y\) 询问 \([a,x+y] ...

  9. 【LOJ】#3043. 「ZJOI2019」线段树

    LOJ#3043. 「ZJOI2019」线段树 计数转期望的一道好题-- 每个点设两个变量\(p,q\)表示这个点有\(p\)的概率有标记,有\(q\)的概率到祖先的路径上有个标记 被覆盖的点$0.5 ...

随机推荐

  1. node中fs内置模块

    Node.js内置的fs模块就是文件系统模块,负责读写文件. 和所有其它JavaScript模块不同的是,fs模块同时提供了异步和同步的方法. 回顾一下什么是异步方法.因为JavaScript的单线程 ...

  2. Postman中的全局/环境/集合变量的使用及优先级

    变量的使用场景 Postman的变量主要用于参数化和关联 应用1: 常用变量可以按使用范围设置成集合或全局变量 应用2: 一套接口要在不同的环境上测试时, 可以新建两个环境,比如test环境和stag ...

  3. Python之从继承到C3算法

    在Python2.X和Python3.X有很多不同的地方,其中一个区别就是和继承有关. 在Python3.X中,一个类如果没有指明其继承哪个类的时候,其默认就是继承object类. 而在Python2 ...

  4. 如何简单的在linux上安装jdk并配置环境变量

    这篇文章是为了给我一会自己安装的时候方便使用的,所以内容很简单,平时在wendows系统上安装很容易,但是换到linux系统上面就蒙圈了. 一.下载jdk文件 我这提供的是官方的地址:http://w ...

  5. ANDROID_ID

    在设备首次启动时,系统会随机生成一个64位的数字,并把这个数字以16进制字符串的形式保存下来,这个16进制的字符串就是ANDROID_ID,当设备被wipe后该值会被重置.可以通过下面的方法获取: i ...

  6. webpack4 打包 library 遇到的坑

    output: { publicPath: '/', path: path.join(__dirname, 'lib'), filename: 'chart.js', library: 'tchart ...

  7. antd源码分析之——标签页(tabs 2.Tabs关键组件功能实现)

    由于ant Tabs组件结构较复杂,共分三部分叙述,本文为目录中第二部分(高亮) 目录 一.组件结构 antd代码结构 rc-ant代码结构 1.组件树状结构 2.Context使用说明 3.rc-t ...

  8. MAC为Apache2服务器配置多个虚拟主机

    Mac 下自带的 Apache 配置 2016年01月25日 00:25:03 阅读数:1292 参考: http://www.cnblogs.com/snandy/archive/2012/11/1 ...

  9. 服务器开启FTP功能

    介绍几个比较完整的教程链接 Windows Server 2012 之文件服务器(FTP)

  10. DB2 SQL 错误(SQLCODE:-964,SQLSTATE:57011)处理方法

    故障现象描述: 执行 SQL 语句时,出现类似如下错误消息. 指令 SQL:insert into t_stat_file_temp SQLSTATE:57011,供应商错误代码:-964 DB2 S ...