题目传送门

题目大意

给出\(n\),求出对于任意\(t\in[1,n]\),点数为\(t\)的弱联通\(\texttt{DAG}\)个数。答案对\(998244353\)取模。

\(n\le 10^5\)

思路

看到\(\texttt{Karry5307}\)的题解里面有很多小问题(但这并不影响\(\texttt {Karry AK IOI}\)),这里给一篇可能没有什么错误的题解。

我们发现直接求似乎不是很好求,我们发现弱连通图组合在一起的话,就相当于一个不保证联通的\(\texttt{DAG}\),于是我们的目标就是如何求出不保证联通性的\(\texttt{DAG}\)的个数。

我们设\(g_n\)为有\(n\)个点的不保证联通的\(\texttt{DAG}\)的个数,我们可以得到转移式:

\[g_n=\sum_{i=1}^{n} \binom{n}{i}2^{i(n-i)}g_{n-i}(-1)^{i-1}
\]

这个式子的意思就是,我们可以先选\(i\)个点入入度为\(0\),然后其余的点构成\(\texttt{DAG}\),这两部分之间的边随便连不连都能满足条件。但是我们并不能恰好有\(i\)个点入度为\(0\),我们只能保证至少有\(i\)个点入度为\(0\),所以我们就需要容斥一下。

我们发现这个式子中最难看的就是\(2^{i(n-i)}\),而我们发现这个可以用\(\texttt{Bluestein}\)拆成\(\frac{(\sqrt{2})^{n^2}}{(\sqrt{2})^{i^2}(\sqrt{2})^{(n-i)^2}}\),当然如果你喜欢的话你也可以拆成:\(\frac{2^{\binom{n}{2}}}{2^{\binom{i}{2}}2^{\binom{n-i}{2}}}\)。我用的是第一种拆成平方的方法。

于是,式子就变成了:

\[g_n=\sum_{i=1}^{n} \binom{n}{i}\frac{(\sqrt{2})^{n^2}}{(\sqrt{2})^{i^2}(\sqrt{2})^{(n-i)^2}}g_{n-i}(-1)^{i-1}
\]

而我们又有:

\[\sqrt{2}\equiv 116195171\pmod {998244353}
\]

所以我们暂时还没有碰到什么困难。

我们发现我们如果设:

\[G(x)=\sum_{i=0}^{n} \frac{g_i}{(\sqrt{2})^{i^2}i!}x^i
\]
\[H(x)=\sum_{i=1}^{n} \frac{(-1)^{i-1}}{(\sqrt{2})^{i^2}i!}x^i
\]

我们就可以得到:

\[G(x)=G(x)H(x)+1
\]

这个代一下就可以得到了。

于是,我们就得到:

\[G(x)=\frac{1}{1-H(x)}
\]

于是,我们就可以求到\(g_i\)了。我们考虑弱连通图和不保证连通性的\(\texttt{DAG}\)之间的关系。我们发现,其实不保证连通性的\(\texttt{DAG}\)就是把一堆弱连通图揉成一坨,于是,如果我们设\(F(x)\)为\(\{g_{0,1,2,...,n}\}\)的指数生成函数,那么,弱连通图的指数型生成函数就是:

\[\ln F(x)
\]

于是,我们就在\(\Theta(n\log n)\)的时间复杂度内解决了这个问题。

\(\text {Code}\)

#include <bits/stdc++.h>
using namespace std; #define SZ(x) ((int)x.size())
#define Int register int
#define sqr2 116195171
#define mod 998244353
#define MAXN 1000005 int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int k){
int res = 1;for (;k;k >>= 1,a = 1ll * a * a % mod) if (k & 1) res = 1ll * res * a % mod;
return res;
}
int inv (int x){return qkpow (x,mod - 2);} typedef vector <int> poly; int rev[MAXN]; void ntt (poly &f,int lim,int type){
#define G 3
#define Gi 332748118
int l = log2 (lim);
for (Int i = 0;i < lim;++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << l - 1);
for (Int i = 0;i < lim;++ i) if (i < rev[i]) swap (f[i],f[rev[i]]);
for (Int i = 1;i < lim;i <<= 1){
int Wn = qkpow (type == 1 ? G : Gi,(mod - 1) / (i << 1));
for (Int j = 0;j < lim;j += i << 1)
for (Int k = 0,w = 1;k < i;++ k,w = 1ll * w * Wn % mod){
int x = f[j + k],y = 1ll * w * f[i + j + k] % mod;
f[j + k] = (x + y) % mod,f[i + j + k] = (x + mod - y) % mod;
}
}
if (type == 1) return ;
for (Int i = 0,Inv = inv (lim);i < lim;++ i) f[i] = 1ll * f[i] * Inv % mod;
#undef G
#undef Gi
} poly operator + (poly a,poly b){
a.resize (max (SZ (a),SZ (b)));
for (Int i = 0;i < SZ (b);++ i) a[i] = add (a[i],b[i]);
return a;
} poly operator - (poly a,poly b){
a.resize (max (SZ (a),SZ (b)));
for (Int i = 0;i < SZ (b);++ i) a[i] = dec (a[i],b[i]);
return a;
} poly operator * (poly a,int b){
for (Int i = 0;i < SZ (a);++ i) a[i] = mul (a[i],b);
return a;
} poly operator * (poly a,poly b){
int d = SZ (a) + SZ (b) - 1,lim = 1;while (lim < d) lim <<= 1;
a.resize (lim),b.resize (lim);
ntt (a,lim,1),ntt (b,lim,1);
for (Int i = 0;i < lim;++ i) a[i] = mul (a[i],b[i]);
ntt (a,lim,-1),a.resize (d);
return a;
} poly operator << (poly a,int n){
a.resize (SZ (a) + n);
for (Int i = SZ (a) - 1;~i;-- i) a[i] = (i >= n ? a[i - n] : 0);
return a;
} poly inv (poly a,int n){
poly b(1,inv (a[0])),c;
for (Int l = 4;(l >> 2) < n;l <<= 1){
c.resize (l >> 1);
for (Int i = 0;i < (l >> 1);++ i) c[i] = i < n ? a[i] : 0;
c.resize (l),b.resize (l);
ntt (c,l,1),ntt (b,l,1);
for (Int i = 0;i < l;++ i) b[i] = mul (b[i],dec (2,mul (b[i],c[i])));
ntt (b,l,-1),b.resize (l >> 1);
}
b.resize (n);
return b;
} poly inv (poly a){return inv (a,SZ (a));}
poly der (poly a){
for (Int i = 0;i < SZ (a) - 1;++ i) a[i] = mul (a[i + 1],i + 1);
a.pop_back ();return a;
}
poly ine (poly a){
a.push_back (0);
for (Int i = SZ (a) - 1;i;-- i) a[i] = mul (a[i - 1],inv (i));
a[0] = 0;return a;
} poly ln (poly a,int n){
a = ine (der (a) * inv (a));
a.resize (n);
return a;
}
poly ln (poly a){return ln (a,SZ (a));} poly exp (poly a,int n){
poly b (1,1),c;
for (Int l = 2;(l >> 1) < n;l <<= 1){
b.resize (l),c = ln (b);
for (Int i = 0;i < l;++ i) c[i] = dec (i < n ? a[i] : 0,c[i]);
c[0] = add (c[0],1);
b = b * c,b.resize (l);
}
b.resize (n);
return b;
}
poly exp (poly a){return exp (a,SZ (a));} template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} poly H;
int n,fac[MAXN],ifac[MAXN]; signed main(){
read (n);
fac[0] = 1;for (Int i = 1;i <= n;++ i) fac[i] = mul (fac[i - 1],i);
ifac[n] = inv (fac[n]);for (Int i = n;i;-- i) ifac[i - 1] = mul (ifac[i],i);
H.resize (n + 1);for (Int i = 1;i <= n;++ i) H[i] = inv (mul (fac[i],qkpow (sqr2,1ll * i * i % (mod - 1)))),H[i] = i & 1 ? mod - H[i] : H[i];
H[0] = 1,H = inv (H);for (Int i = 0;i <= n;++ i) H[i] = mul (H[i],qkpow (sqr2,1ll * i * i % (mod - 1)));H = ln (H);
for (Int i = 1;i <= n;++ i) write (mul (H[i],fac[i])),putchar ('\n');
return 0;
}

一个小小的总结

其实做了几道多项式与图计数的题目之后可以发现,对于一些不是很好求到的答案我们采用的办法就是把所求多项式与更好求的图的多项式建立关系,从而反推出该图的生成函数。反推的方法就不计其数了。

题解 有标号DAG计数的更多相关文章

  1. 有标号DAG计数(生成函数)

    有标号DAG计数(生成函数) luogu 题解时间 首先考虑暴力,很容易得出 $ f[ i ] = \sum\limits_{ j = 1 }^{ i } ( -1 )^{ j - 1 } \bino ...

  2. 有标号DAG计数 [容斥原理 子集反演 组合数学 fft]

    有标号DAG计数 题目在COGS上 [HZOI 2015]有标号的DAG计数 I [HZOI 2015] 有标号的DAG计数 II [HZOI 2015]有标号的DAG计数 III I 求n个点的DA ...

  3. P6295 有标号 DAG 计数

    P6295 有标号 DAG 计数 题意 求 \(n\) 个点有标号弱联通 DAG 数量. 推导 设 \(f_i\) 表示 \(i\) 个点有标号 DAG 数量(不保证弱联通),有: \[f(i)=\s ...

  4. 洛谷 P6295 - 有标号 DAG 计数(生成函数+容斥+NTT)

    洛谷题面传送门 看到图计数的题就条件反射地认为是不可做题并点开了题解--实际上这题以我现在的水平还是有可能能独立解决的( 首先连通这个条件有点棘手,我们尝试把它去掉.考虑这题的套路,我们设 \(f_n ...

  5. P6295-有标号 DAG 计数【多项式求逆,多项式ln】

    正题 题目链接:https://www.luogu.com.cn/problem/P6295 题目大意 求所有\(n\)个点的弱联通\(DAG\)数量. \(1\leq n\leq 10^5\) 解题 ...

  6. 【题解】有标号的DAG计数4

    [HZOI 2015] 有标号的DAG计数 IV 我们已经知道了\(f_i\)表示不一定需要联通的\(i\)节点的dag方案,考虑合并 参考[题解]P4841 城市规划(指数型母函数+多项式Ln),然 ...

  7. 【题解】有标号的DAG计数3

    [HZOI 2015] 有标号的DAG计数 III 我们已经知道了\(f_i\)表示不一定需要联通的\(i\)节点的dag方案,考虑合并 参考[题解]P4841 城市规划(指数型母函数+多项式Ln), ...

  8. 【题解】有标号的DAG计数1

    [HZOI 2015] 有标号的DAG计数 I 设\(f_i\)为\(i\)个点时的DAG图,(不必联通) 考虑如何转移,由于一个DAG必然有至少一个出度为\(0\)的点,所以我们钦定多少个出度为\( ...

  9. 【题解】有标号的DAG计数2

    [HZOI 2015] 有标号的DAG计数 II \(I\)中DP只有一个数组, \[ dp_i=\sum{i\choose j}2^{j(i-j)}dp_{i-j}(-1)^{j+1} \] 不会. ...

随机推荐

  1. 【C语言】第2章 算法 — 程序的灵魂

    第2章 算法 - 程序的灵魂 一个程序主要包括以下两方面的信息: 对数据的描述.在程序中要指定用到哪些数据以及这些数据的类型和数据的组织形式 也就是数据结构(data structure) 对操作的描 ...

  2. UVA1620 Lazy Susan(结论证明)

    结论: 当 \(n\geq 6\) 时,若 \(n\) 是奇数且输入序列的逆序对数是奇数,则无解,否则有解. 当 \(n=4\) 或 \(n=5\) 时,答案个数及其有限,只有这个环是 \(1\) 到 ...

  3. Python语法之选择和循环(if、while)

    前言:在程序开发中,一共有三种流程方式: 顺序:从上向下,顺序执行代码 分支:根据条件判断,决定执行代码的分支 循环:让特定代码重复执行(解决程序员重复工作) 1.判断的定义: 如果条件满足,才能做某 ...

  4. H5、C3基础知识笔记

    HTML5 本文内容参考于"HTML5|W3scool"教程 简介 是最新的 HTML 标准,拥有新的语义.图形以及多媒体元素 提供了新的 API 简化了 web 应用程序的搭建 ...

  5. HTB Hack The Box -- Oopsiec

    信息收集 开放了22ssh,80端口,其中ssh有弱口令爆破端口 先打开网页,然后进行目录爆破,在这期间先看一下网页的大概信息 没爆到什么有用的东西,但是有uploads文件夹说明是不是说明有文件上传 ...

  6. 比年轻更年轻,快看能否接棒B站?

    撰文 |懂懂 编辑 | 秦言 来源:懂懂笔记 背靠超新Z世代,快看能否接棒B站? 国漫什么时候能追上日漫? 国漫作者真能挣到钱吗? 国漫什么时候才能走向世界? 这是中国漫画从业者的"灵魂三问 ...

  7. 5-21python数据类型

    一.字符串,是不可变数据类型,所有字符串的方法都不会修改字符串的值,使用字符串的方法后都是生成了一个新的字符串.就因为字符串是不可变变量! 字符串的方法 1. strip(),默认去空格,但是当()中 ...

  8. 企业级镜像仓库 harbor

    企业级镜像仓库 harbor 前言 a. 本文主要为 Docker的视频教程 笔记. b. 环境为 CentOS 7.0 云服务器 c. 上一篇:跨 Docker 宿主机网络 overlay 类型 h ...

  9. 洛谷P1583——魔法照片(结构体排序)

    https://www.luogu.org/problem/show?pid=1583#sub 题目描述 一共有n(n≤20000)个人(以1--n编号)向佳佳要照片,而佳佳只能把照片给其中的k个人. ...

  10. PTA——c++函数

    1.在C++中,关于下列设置缺省参数值的描述中,()是正确的. 在指定了缺省值的参数右边,不能出现没有指定缺省值的参数: 2.使用地址作为实参传给形参,下列说法正确的是() 实参与形参操作的是同一对象 ...