UOJ#335. 【清华集训2017】生成树计数 多项式,FFT,下降幂,分治
原文链接www.cnblogs.com/zhouzhendong/p/UOJ335.html
前言
CLY大爷随手切这种题。
日常被CLY吊打系列。
题解
首先从 pruffer 编码的角度考虑这个问题。
pruffer 编码的长度为 $n-2$ ,如果点 $i$ 在 pruffer 编码中出现了 $d_i - 1$ 次,那么点 $i$ 的度数就是 $d_i$ ,对答案的贡献次数就是 $\binom {n-2}{d_i}a_i ^ {d_i}$ 。
于是自然想到用 EGF 做这个题。设
$$f_k(x) = \sum_{i=0}^{n-2} a_k ^ i (i+1) ^ m \frac{x^i}{i!}\\ g_k = \sum_{i=0}^{n-2} a_k ^ i (i+1) ^ {2m} \frac {x^i} {i!}$$
则答案就等于
$$\left(\prod_{i=1}^n a_i \right) \times \sum_{i=1}^n g_i(x) \prod_{j=1,j\neq i}^{n} f_j(x)$$
这个 EGF 的 $n-2$ 次项的系数。(注意是多项式系数乘以 $(n-2)!$)
假设
$$(x + 1) ^ m = \sum_{j=0}^m v_j i ^ {\underline{j}}$$
我们考虑对 $f_k(x)$ 操作一波,把:
$$\begin{eqnarray*}f_k(x) &=& \sum_{i=0}^{n-2} \frac{(a_kx)^i}{i!}\sum_{j=0}^m v_j i^{\underline{j}}\\&=&\sum_{j=0}^m v_j (a_kx) ^ j \sum_{i=0}^{n-2}\frac{(a_kx) ^ i}{i!}\\&=& \sum_{j=0}^m v_j(a_kx) ^ j e ^ {a_kx} \pmod {x^n-2} \end{eqnarray*}$$
设
$$(x + 1) ^2m = \sum_{j=0}^2m V_j i ^ {\underline{j}}$$
同理,我们对 $g_k(x)$ 也做类似的操作,可以得到
$$ans = \left(\prod_{i=1}^n a_i \right) e ^ {x\sum_{i=1}^n a_i} \sum_{i=1}^n \left(\sum_{j=0}^{2m} V_j (a_ix)^j \right)\prod_{k=1,k\neq i }^n \left(\sum_{t=0}^{m} v_j (a_kx)^t \right)$$
分治 FFT 即可。
时间复杂度 $O(nm\log ^2 n)$ 。
代码
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define fi first
#define se second
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
#define outarr(a,L,R) printf(#a"[%d...%d] = ",L,R);\
For(_v2,L,R)printf("%d ",a[_v2]);puts("");
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef vector <int> vi;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=30005,M=65,Len=1<<16,mod=998244353;
int Pow(int x,int y){
int ans=1;
for (;y;y>>=1,x=(LL)x*x%mod)
if (y&1)
ans=(LL)ans*x%mod;
return ans;
}
void Add(int &x,int y){
if ((x+=y)>=mod)
x-=mod;
}
void Del(int &x,int y){
if ((x-=y)<0)
x+=mod;
}
int del(int x){
return x<0?x+mod:x;
}
int add(int x){
return x>=mod?x-mod:x;
}
namespace poly{
int R[Len],w[Len];
void init(int n,int d){
For(i,1,n-1)
R[i]=(R[i>>1]>>1)|((i&1)<<(d-1));
w[0]=1,w[1]=Pow(3,(mod-1)/n);
For(i,2,n-1)
w[i]=(LL)w[i-1]*w[1]%mod;
}
void FFT(int *a,int n,int flag){
if (flag<0)
reverse(w+1,w+n);
For(i,0,n-1)
if (i<R[i])
swap(a[i],a[R[i]]);
for (int t=n>>1,d=1;d<n;d<<=1,t>>=1)
for (int i=0;i<n;i+=d<<1)
for (int j=0;j<d;j++){
int tmp=(LL)w[t*j]*a[i+j+d]%mod;
a[i+j+d]=del(a[i+j]-tmp);
Add(a[i+j],tmp);
}
if (flag<0){
reverse(w+1,w+n);
int inv=Pow(n,mod-2);
For(i,0,n-1)
a[i]=(LL)a[i]*inv%mod;
}
}
}
using poly::FFT;
int n,m;
int a[N];
int C[M][M],S[M][M],Fac[N],Inv[N];
int v1[M],v2[M];
void prework(){
int n=M-1;
For(i,0,n)
C[i][0]=C[i][i]=1;
S[0][0]=1;
For(i,1,n)
For(j,1,i){
C[i][j]=add(C[i-1][j-1]+C[i-1][j]);
Add(S[i][j]=S[i-1][j-1],(LL)S[i-1][j]*j%mod);
}
n=m;
For(i,0,n)
For(j,0,i)
Add(v1[j],(LL)C[n][i]*S[i][j]%mod);
n=m*2;
For(i,0,n)
For(j,0,i)
Add(v2[j],(LL)C[n][i]*S[i][j]%mod);
n=N-1;
for (int i=Fac[0]=1;i<=n;i++)
Fac[i]=(LL)Fac[i-1]*i%mod;
Inv[n]=Pow(Fac[n],mod-2);
Fod(i,n,1)
Inv[i-1]=(LL)Inv[i]*i%mod;
}
int f[N*M],g[N*M];
int Hash(int i,int j){
return (i-1)*(m*2+1)+j;
}
int f1[Len],f2[Len],g1[Len],g2[Len],f3[Len],g3[Len];
int Solve(int L,int R){
if (L==R)
return m*2;
int mid=(L+R)>>1;
int l1=Solve(L,mid),l2=Solve(mid+1,R);
int p1=Hash(L,0),p2=Hash(mid+1,0);
int s,d;
for (s=1,d=0;s<l1+l2+1;s<<=1,d++);
poly::init(s,d);
For(i,0,s-1)
f1[i]=f2[i]=g1[i]=g2[i]=0;
For(i,0,l1)
f1[i]=f[i+p1],g1[i]=g[i+p1];
For(i,0,l2)
f2[i]=f[i+p2],g2[i]=g[i+p2];
FFT(f1,s,1),FFT(g1,s,1);
FFT(f2,s,1),FFT(g2,s,1);
For(i,0,s-1){
f3[i]=(LL)f1[i]*f2[i]%mod;
g3[i]=((LL)f1[i]*g2[i]+(LL)g1[i]*f2[i])%mod;
}
FFT(f3,s,-1),FFT(g3,s,-1);
int pR=Hash(R+1,0);
For(i,p1,pR-1)
f[i]=g[i]=0;
For(i,0,s-1)
if (i+p1<pR)
f[i+p1]=f3[i],g[i+p1]=g3[i];
else
break;
int len=pR-1;
while (!f[len]&&!g[len]&&len>p1)
len--;
while (len-p1>n)
f[len]=g[len]=0,len--;
return len-p1;
}
int Exp[Len];
int main(){
n=read(),m=read();
For(i,1,n)
a[i]=read();
prework();
For(i,1,n){
int tmp=1;
For(j,0,m){
f[Hash(i,j)]=(LL)v1[j]*tmp%mod;
tmp=(LL)tmp*a[i]%mod;
}
tmp=1;
For(j,0,m*2){
g[Hash(i,j)]=(LL)v2[j]*tmp%mod;
tmp=(LL)tmp*a[i]%mod;
}
}
int len=Solve(1,n),Sum=0;
For(i,1,n)
Add(Sum,a[i]);
For(i,0,n)
Exp[i]=(LL)Inv[i]*Pow(Sum,i)%mod;
int L=1,d=0;
for (;L<len+n+1;L<<=1,d++);
poly::init(L,d);
FFT(Exp,L,1),FFT(g,L,1);
For(i,0,L-1)
g[i]=(LL)g[i]*Exp[i]%mod;
FFT(g,L,-1);
int ans=(LL)g[n-2]*Fac[n-2]%mod;
For(i,1,n)
ans=(LL)ans*a[i]%mod;
cout<<ans<<endl;
return 0;
}
UOJ#335. 【清华集训2017】生成树计数 多项式,FFT,下降幂,分治的更多相关文章
- 洛谷 P4002 - [清华集训2017]生成树计数(多项式)
题面传送门 神题. 考虑将所有连通块缩成一个点,那么所有连好边的生成树在缩点之后一定是一个 \(n\) 个点的生成树.我们记 \(d_i\) 为第 \(i\) 个连通块缩完点之后的度数 \(-1\), ...
- UOJ269 清华集训2016 如何优雅地求和 下降幂多项式、NTT
代码 神仙题? 看到连续的点值,那么一定是要利用到连续点值的性质,可以考虑下降幂多项式,即考虑多项式\(F(x) = \sum\limits_{i=0}^m a_ix^{\underline i}\) ...
- Loj 2320.「清华集训 2017」生成树计数
Loj 2320.「清华集训 2017」生成树计数 题目描述 在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\ ...
- 【UOJ#340】【清华集训2017】小 Y 和恐怖的奴隶主(矩阵快速幂,动态规划)
[UOJ#340][清华集训2017]小 Y 和恐怖的奴隶主(矩阵快速幂,动态规划) 题面 UOJ 洛谷 题解 考虑如何暴力\(dp\). 设\(f[i][a][b][c]\)表示当前到了第\(i\) ...
- Luogu P5296 [北京省选集训2019]生成树计数
Luogu P5296 [北京省选集训2019]生成树计数 题目链接 题目大意:给定每条边的边权.一颗生成树的权值为边权和的\(k\)次方.求出所有生成树的权值和. 我们列出答案的式子: 设\(E\) ...
- [UOJ#274][清华集训2016]温暖会指引我们前行
[UOJ#274][清华集训2016]温暖会指引我们前行 试题描述 寒冬又一次肆虐了北国大地 无情的北风穿透了人们御寒的衣物 可怜虫们在冬夜中发出无助的哀嚎 “冻死宝宝了!” 这时 远处的天边出现了一 ...
- Loj #2331. 「清华集训 2017」某位歌姬的故事
Loj #2331. 「清华集训 2017」某位歌姬的故事 IA 是一名会唱歌的女孩子. IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符, ...
- Loj #2324. 「清华集训 2017」小 Y 和二叉树
Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...
- Loj #2321. 「清华集训 2017」无限之环
Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...
随机推荐
- Vue(小案例_vue+axios仿手机app)_Vuex优化购物车功能
一.前言 1.用vuex实现加入购物车操作 2.购物车详情页面 3.点击删除按钮,删除购物详情页面里的对应商品 二.主要内容 1.用vuex加入购物车 (1)在src ...
- 转载:curl 模拟请求
一般情况下我们会在网页上请求后台接口,但是对于需要进行多次测试的人来说,每一次都要在网页上模拟请求,是存在很大局限性的.因此,我们需要学会模拟请求,以达到跟实际请求一样的效果. 1. curl的用法 ...
- Python正则表达式指南(转)
目录 Python正则表达式指南(转) 0.防走丢 1. 正则表达式基础 1.1. 简单介绍 1.2. 数量词的贪婪模式与非贪婪模式 1.3. 反斜杠的困扰 1.4. 匹配模式 2. re模块 2.1 ...
- MapReduce-TextInputFormat 切片机制
MapReduce 默认使用 TextInputFormat 进行切片,其机制如下 (1)简单地按照文件的内容长度进行切片 (2)切片大小,默认等于Block大小,可单独设置 (3)切片时不考虑数据集 ...
- VMware 安装Linux系统 CentOS
VMware 安装Linux系统 CentOS 1. 下载镜像系统 centos镜像下载地址:https://www.centos.org/download/ 选择DVD下载即可 linux各版本下 ...
- 解决:Using where; Using join buffer (Block Nested Loop)
问题:left join 时候触发了全表查询导致很慢 解决:Using where; Using join buffer (Block Nested Loop) 总结:其实就是把left join 改 ...
- Koa与Node.js开发实战(3)——Nunjucks模板在Koa中的应用(视频演示)
技术架构: 在Koa中应用Nunjucks,需要先把Nunjucks集成为符合Koa规格的中间件(Middleware),从本质上来讲,集成后的中间件的作用是给上下文对象绑定一个render(vi ...
- 【转】Unity四元数和向量相乘作用及其运算规则
作用:四元数和向量相乘表示这个向量按照这个四元数进行旋转之后得到的新的向量. 比如:向量vector3(0,0,10),绕着Y轴旋转90度,得到新的向量是vector3(10,0,0). 在unity ...
- 20155324《网络对抗》Exp1 PC平台逆向破解(5)M
20155324<网络对抗>Exp1 PC平台逆向破解(5)M 实验目标 本次实践的对象是一个名为~pwn1~的~linux~可执行文件. 该程序正常执行流程是:~main~调用~foo~ ...
- Python Cookbook 数据结构和算法
1.查找最大或最小的N个元素 import heapq nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2] print(heapq.nlargest(3, n ...