有标号的DAG计数系列问题

传送门

II

设 \(f_i\) 表示 \(i\) 个点的答案
那么枚举至少 \(j\) 个点的出度为 \(0\)
\[\sum_{j=0}^{i}(-1)^j\binom{i}{j}f_{i-j}2^{(i-j)j}=0\]
所以
\[f_i=\sum_{j=1}^{i}(-1)^{j+1}\binom{i}{j}f_{i-j}2^{(i-j)j}\]

\[\frac{f_i}{i!}=\sum_{j=0}^{i-1}\frac{f_j}{j!}\frac{(-1)^{i-j+1}}{(i-j)!}2^{(i-j)j}\]
现在的难处在于 \(2^{(i-j)j}\) 的拆分
可以发现
\[2^{(i-j)j}=\frac{2^{\frac{i^2}{2}}}{2^{\frac{(i-j)^2}{2}}2^{\frac{j^2}{2}}}\]
所以
\[\frac{f_i}{i!\sqrt{2}^{i^2}}=\sum_{j=0}^{i-1}\frac{f_j}{j!\sqrt{2}^{j^2}}\frac{(-1)^{i-j+1}}{(i-j)!\sqrt{2}^{(i-j)^2}}\]
求出 \(2\) 在模 \(998244353\) 意义下的二次剩余然后多项式求逆即可
二次剩余没有学QwQ直接暴力预处理出来即可

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn(4e5 + 5);
const int mod(998244353);
const int res2(116195171);
const int invres2(557219762);

inline void Inc(int &x, int y) {
    if ((x += y) >= mod) x -= mod;
}

inline int Pow(ll x, int y) {
    register ll ret = 1;
    for (; y; y >>= 1, x = x * x % mod)
        if (y & 1) ret = ret * x % mod;
    return ret;
}

int a[maxn], b[maxn], w[2][maxn], deg, r[maxn], l;

inline void Init(int n) {
    register int i, k, wn, iwn;
    for (deg = 1, l = 0; deg < n; deg <<= 1) ++l;
    for (i = 0; i < deg; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
    for (i = 1; i < deg; i <<= 1) {
        w[0][0] = w[1][0] = 1;
        wn = Pow(3, (mod - 1) / (i << 1)), iwn = Pow(wn, mod - 2);
        for (k = 1; k < i; ++k) {
            w[0][deg / i * k] = 1LL * w[0][deg / i * (k - 1)] * wn % mod;
            w[1][deg / i * k] = 1LL * w[1][deg / i * (k - 1)] * iwn % mod;
        }
    }
}

inline void NTT(int *p, int opt) {
    register int i, j, k, t, wn, x, y;
    for (i = 0; i < deg; ++i) if (r[i] < i) swap(p[r[i]], p[i]);
    for (i = 1; i < deg; i <<= 1)
        for(t = i << 1, j = 0; j < deg; j += t)
            for (k = 0; k < i; ++k) {
                wn = w[opt == -1][deg / i * k];
                x = p[j + k], y = 1LL * wn * p[i + j + k] % mod;
                p[j + k] = x + y, p[i + j + k] = x - y;
                if (p[j + k] >= mod) p[j + k] -= mod;
                if (p[i + j + k] < 0) p[i + j + k] += mod;
            }
    if (opt == -1) {
        wn = Pow(deg, mod - 2);
        for (i = 0; i < deg; ++i) p[i] = 1LL * p[i] * wn % mod;
    }
}

int n, f[maxn], g[maxn], fac[maxn], ifac[maxn];

void Inv(int *p, int *q, int len) {
    if (len == 1) {
        q[0] = Pow(p[0], mod - 2);
        return;
    }
    Inv(p, q, len >> 1);
    register int i, tmp = len << 1;
    for (i = 0; i < len; ++i) a[i] = p[i], b[i] = q[i];
    Init(tmp), NTT(a, 1), NTT(b, 1);
    for (i = 0; i < tmp; ++i) a[i] = 1LL * a[i] * b[i] % mod * b[i] % mod;
    NTT(a, -1);
    for (i = 0; i < len; ++i) q[i] = (2LL * q[i] + mod - a[i]) % mod;
    for (i = 0; i < tmp; ++i) a[i] = b[i] = 0;
}

int main() {
    register int i, len;
    scanf("%d", &n), f[0] = fac[0] = ifac[0] = ifac[1] = 1;
    for (i = 2; i <= n; ++i) ifac[i] = 1LL * (mod - mod / i) * ifac[mod % i] % mod;
    for (i = 1; i <= n; ++i) fac[i] = 1LL * fac[i - 1] * i % mod, ifac[i] = 1LL * ifac[i - 1] * ifac[i] % mod;
    for (i = 0; i <= n; ++i) {
        f[i] = 1LL * ifac[i] * Pow(invres2, 1LL * i * i % (mod - 1)) % mod;
        if (i & 1) f[i] = mod - f[i];
    }
    for (len = 1; len <= n; len <<= 1);
    Inv(f, g, len);
    printf("%lld\n", 1LL * fac[n] * Pow(res2, 1LL * n * n % (mod - 1)) % mod * g[n] % mod);
    return 0;
}

IV

设 \(f_i\) 表示不要求连通的 \(i\) 个点 的 \(DAG\) 的方案数
直接 \(II\) 那样求出来
设 \(g_i\) 表示 \(i\) 个点的答案

方法一

钦定第一个连通块的一个点
\[g_i=f_i-\sum_{j=1}^{i-1}g_jf_{i-j}\binom{i-1}{j-1}\]
组合数拆整理得到
\[\frac{f_i}{(i-1)!}=\sum_{j=1}^{i}\frac{g_j}{(i-1)!}\frac{f_{i-j}}{(i-j)!}\]
直接多项式求逆后卷起来就好了

方法二


\[F(x)=\sum_{i=0}^{\infty}f_i\frac{x^i}{i!}\]
\[G(x)=\sum_{i=0}^{\infty}g_i\frac{x^i}{i!}\]
那么
\[F(x)=e^{G(x)},G(x)=ln F(x)\]

类似的结论还有
简单无向图的指数级生成函数
\[G(x)=\sum_{i=0}^{\infty}2^{\binom{i}{2}}\frac{x^i}{i!}\]
简单无向连通图的指数级生成函数
\[F(x)=\sum_{i=0}^{\infty}f_i\frac{x^i}{i!}\]
\[G(x)=e^{F(x)}, F(x)=ln G(x)\]

暂时不会证明

方法一

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn(4e5 + 5);
const int mod(998244353);
const int res2(116195171);
const int invres2(557219762);

inline void Inc(int &x, int y) {
    if ((x += y) >= mod) x -= mod;
}

inline int Pow(ll x, int y) {
    register ll ret = 1;
    for (; y; y >>= 1, x = x * x % mod)
        if (y & 1) ret = ret * x % mod;
    return ret;
}

int a[maxn], b[maxn], w[2][maxn], deg, r[maxn], l;

inline void Init(int n) {
    register int i, k, wn, iwn;
    for (deg = 1, l = 0; deg < n; deg <<= 1) ++l;
    for (i = 0; i < deg; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
    for (i = 1; i < deg; i <<= 1) {
        w[0][0] = w[1][0] = 1;
        wn = Pow(3, (mod - 1) / (i << 1)), iwn = Pow(wn, mod - 2);
        for (k = 1; k < i; ++k) {
            w[0][deg / i * k] = 1LL * w[0][deg / i * (k - 1)] * wn % mod;
            w[1][deg / i * k] = 1LL * w[1][deg / i * (k - 1)] * iwn % mod;
        }
    }
}

inline void NTT(int *p, int opt) {
    register int i, j, k, t, wn, x, y;
    for (i = 0; i < deg; ++i) if (r[i] < i) swap(p[r[i]], p[i]);
    for (i = 1; i < deg; i <<= 1)
        for(t = i << 1, j = 0; j < deg; j += t)
            for (k = 0; k < i; ++k) {
                wn = w[opt == -1][deg / i * k];
                x = p[j + k], y = 1LL * wn * p[i + j + k] % mod;
                p[j + k] = x + y, p[i + j + k] = x - y;
                if (p[j + k] >= mod) p[j + k] -= mod;
                if (p[i + j + k] < 0) p[i + j + k] += mod;
            }
    if (opt == -1) {
        wn = Pow(deg, mod - 2);
        for (i = 0; i < deg; ++i) p[i] = 1LL * p[i] * wn % mod;
    }
}

int n, f[maxn], g[maxn], h[maxn], fac[maxn], ifac[maxn];

void Inv(int *p, int *q, int len) {
    if (len == 1) {
        q[0] = Pow(p[0], mod - 2);
        return;
    }
    Inv(p, q, len >> 1);
    register int i, tmp = len << 1;
    for (i = 0; i < len; ++i) a[i] = p[i], b[i] = q[i];
    Init(tmp), NTT(a, 1), NTT(b, 1);
    for (i = 0; i < tmp; ++i) a[i] = 1LL * a[i] * b[i] % mod * b[i] % mod;
    NTT(a, -1);
    for (i = 0; i < len; ++i) q[i] = (2LL * q[i] + mod - a[i]) % mod;
    for (i = 0; i < tmp; ++i) a[i] = b[i] = 0;
}

int main() {
    register int i, len;
    scanf("%d", &n), f[0] = fac[0] = ifac[0] = ifac[1] = 1;
    for (i = 2; i <= n; ++i) ifac[i] = 1LL * (mod - mod / i) * ifac[mod % i] % mod;
    for (i = 1; i <= n; ++i) fac[i] = 1LL * fac[i - 1] * i % mod, ifac[i] = 1LL * ifac[i - 1] * ifac[i] % mod;
    for (i = 0; i <= n; ++i) {
        f[i] = 1LL * ifac[i] * Pow(invres2, 1LL * i * i % (mod - 1)) % mod;
        if (i & 1) f[i] = mod - f[i];
    }
    for (len = 1; len <= n; len <<= 1);
    Inv(f, g, len);
    for (i = 0; i <= n; ++i) g[i] = 1LL * g[i] * Pow(res2, 1LL * i * i % (mod - 1)) % mod;
    for (memset(f, 0, sizeof(f)), i = n + 1; i < len; ++i) g[i] = 0;
    for (i = 1; i <= n; ++i) f[i] = 1LL * g[i] * i % mod;
    Inv(g, h, len), len <<= 1;
    for (i = 0; i < len; ++i) a[i] = f[i], b[i] = h[i];
    Init(len), NTT(a, 1), NTT(b, 1);
    for (i = 0; i < len; ++i) a[i] = 1LL * a[i] * b[i] % mod;
    NTT(a, -1);
    printf("%lld\n", 1LL * a[n] * fac[n - 1] % mod);
    return 0;
}

原文地址:https://www.cnblogs.com/cjoieryl/p/10078167.html

时间: 2024-09-28 23:28:43

有标号的DAG计数系列问题的相关文章

有标号的DAG计数I~IV

有标号的DAG计数 最近心血来潮来写一写这个玩意儿. 请特别注意定义生成函数时下标的起始位置. 有标号的DAG计数I 求\(n\)点带标号\(DAG\)的数量模\(10007\).\(n\le5000\). 数据范围显然\(O(n^2)\).设\(f_i\)表示答案,枚举\(DAG\)中入度为零的点的数量\(j\),方案数为\(\binom ij\),将图拆成两个部分,前\(j\)个点向后\(i-j\)个点的连边任意,方案数为\(2^{j(i-j)}\),后\(j\)个点构成一张\(DAG\),

有标号的DAG计数 III

Description 给定一正整数n,对n个点有标号的有向无环图进行计数,这里加一个限制:此图必须是弱连通图.输出答案 mod 10007 的结果. Solution 弱连通图即把边变成无向之后成为连通的图 考虑补集转换,用 \(DAG\) 的方案数减去不连通的方案数 设 \(f[i]\) 为大小为 \(i\) 的\(DAG\)的方案数 可以像 \(DAG I\) 那样求出来 \(g[i]\) 为弱连通图的方案数 \(g[n]=f[n]-\sum_{i=1}^{n}g[i]*f[i-j]*C_

[COGS 2353 &amp; 2356] 有标号的DAG计数 容斥原理

COGS 2353 题意 问 n 个点的带标号 DAG 有多少个. n <= 5000 . 分析 DAG 的突破口在于度数为 0 的点, 我们每次将其删去, 则还有一些度数为 0 的点. 设 $f_n$ 为 n 个点的带标号 DAG 个数, 奠基 $f_0 = 1$ , 答案为 $f_n$ . 我们考虑容斥原理, 用至少有 1 个度数为 0 的点的 DAG 个数, 减去至少有 2 个度数为 0 的点的 DAG 个数, 加上至少有 3 个度数为 0 的点的 DAG 个数, ... $$f_n =

有标号的DAG/强连通图计数

COGS索引 一堆神仙容斥+多项式-- 有标号的DAG计数 I 考虑\(O(n^2)\)做法:设\(f_i\)表示总共有\(i\)个点的DAG数量,转移考虑枚举DAG上所有出度为\(0\)的点,剩下的点可以选择连向它,剩下的点之间也可以连边. 但是注意到这样子转移可能会存在剩下的点中有点没有出度的情况,考虑容斥解决:设枚举的出度为\(0\)的点的个数为\(i\)时的容斥系数为\(f_i\),那么一个实际上存在\(x\)个出度为\(0\)的点的DAG的贡献就是\(\sum\limits_{i=1}

有标号的DAG图计数1~4

前言 我什么都不会,菜的被关了起来. 有标号的DAG图I Solution 考虑递推,设\(f_i\)表示i个点的答案,显然这个东西是可以组合数+容斥递推? 设\(f_i\)表示i个点的答案,我们考虑假设现在有j个点入度为1,那么可以选出的点就是一个组合数\(C_i^j\),边的可能性有两种,对应的就是\(2^{j*(i-j)}\),然后接着搞,肯定这样子算会有重复的,所以容斥一下然后和以前的答案乘起来就好了. \(f_i=\sum_{j=1}^{i}f_{i-j}*-1^{j-1}*C_i^j

无标号树的计数原理(组合计数,背包问题,隔板法,树的重心)

闲话 一个计数问题入门级选手来搞这种东西 最初的动力来自高一化学课有机物(滑稽).<同步导练>出了个这样的选择题. 一个结构极其庞大的烷烃(二十几个碳原子),求它的主链长度. 这不是个求树的直径的裸题么?!OI选手扫两眼就出来了,然而别的同学费劲心思找完了还是错的. 于是第一次在常规课中体验到作为OIer的优越感...... 又是一节课,芙蓉姐开始要我们画己烷.庚烷的同分异构体?! 这不是等于要求节点数为\(n\),点度数不超过\(4\)的无标号的无根树个数吗?没见过,但还是有一点DP思想,蒟

cojs 强连通图计数1-2 题解报告

OwO 题目含义都是一样的,只是数据范围扩大了 对于n<=7的问题,我们直接暴力搜索就可以了 对于n<=1000的问题,我们不难联想到<主旋律>这一道题 没错,只需要把方程改一改就可以了 首先我们考虑不合法的方案强连通分量缩点后一定是DAG 考虑子问题:DAG计数 做法可以参考<cojs DAG计数1-4 题解报告> 这里给出转移方程 f(n)=sigma((-1)^(k-1)*C(n,k)*2^(k*(n-k))*f(n-k)) 如果考虑上强连通分量缩点的情况呢? 我

图的计数

无标号计数与带标号计数 无标号就是在观测上不考虑个体的差异, 带标号就是在观测上考虑个体的差异. 对于无标号计数, 我们可以按照某种关键值, 给所有的元素进行定序, 所有无标号计数等价于定序计数. 对于带标号计数, 我们可以任意设置关键值, 所以带标号计数等价于不定序计数. 最常见的例子就是组合与排列, 它们还满足一组特殊的关系: 组合数 * n! = 排列数. 有向图 / 无向图 带标号无向图的 度数的K次方 的和 分析 $ans = n \sum_{d = 0} ^ {n - 1} \bin

51Nod 1806 wangyurzee的树

1806 wangyurzee的树 链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1806 想法:因为$m \le 17$,所以用容斥统计一下.即限定一些$u_i$的度数为$d_i$,然后变成Prufer统计带标号树的计数. #include< cstdio > #define FILE(F) freopen(F".in","r",stdin),freopen(F&quo