有标号DAG计数 [容斥原理 子集反演 组合数学 fft]
有标号DAG计数
题目在COGS上
I
求n个点的DAG(可以不连通)的个数。\(n \le 5000\)
2013年王迪的论文很详细了
感觉想法很神,自己怎么想到啊?
首先要注意到DAG中一类特殊的点:入度为0的点。以这些点来分类统计
先是一种\(O(N^3)\)的dp, \(d(i,j)\) i个点j个入度为0,转移枚举去掉j个后入度为0点的个数,乘上连边情况
在弱化条件时特殊化
\(f(n, S)\) n个点,只有S中的点入度为0
\(g(n,S)\) n个点,至少S中的点入度为0
g(n, S) = \sum_{S \subset T} f(n, T)\quad (1)
\]
子集反演!
\]
我们目标是求\(g(n, \varnothing)\),
代入\((1),(2)\),然后使用枚举集合大小,乘上组合数的技巧,\(m = \mid T\mid, k = \mid S\mid\)。还需要用\(\binom{n}{k} \binom{k}{m} = \binom{n}{m} \binom{n-m} {k-m}\)替换,最后得到
\]
完成!
现在尝试直接考虑这个式子的意义:
\]
II
求n个点的DAG(可以不连通)的个数。\(n \le 10^5\)
当然要用fft啦!分治或者多项式求逆。
\(2^{k(n-k)}\)怎么办?
和hdu那道题类似,把\((n-k)^2 = n^2 - 2nk + k^2\)代入
但这样会出现\(2^{\frac{n}{2}}\), 2的逆元\(\mod P-1\)不存在,所以要求2的二次剩余\(x^2 \equiv 2 \pmod {P-1}\)
III & IIII
求n个点的DAG(必须连通)的个数。\(n \le 5000, n \le 10^5\)
和城市规划类似的思想...
可以不连通 到 连通
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 5005, P = 10007;
inline int read() {
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n, g[N];
int inv[N], fac[N], facInv[N], mi[P+5];
inline int C(int n, int m) {return fac[n] * facInv[m] %P * facInv[n-m] %P;}
int main() {
freopen("DAG.in", "r", stdin);
freopen("DAG.out", "w", stdout);
//freopen("in", "r", stdin);
n = read();
inv[1] = fac[0] = facInv[0] = 1;
for(int i=1; i<=n; i++) {
if(i != 1) inv[i] = (P - P/i) * inv[P%i] %P;
fac[i] = fac[i-1] * i %P;
facInv[i] = facInv[i-1] * inv[i] %P;
}
mi[0] = 1;
for(int i=1; i<=P; i++) mi[i] = mi[i-1] * 2 %P;
g[0] = 1;
for(int i=1; i<=n; i++)
for(int k=1; k<=i; k++) g[i] = (g[i] + ((k&1) ? 1 : -1) * C(i, k) %P * mi[k * (i-k) % (P-1)] %P * g[i-k] %P) %P;
printf("%d\n", (g[n] + P) %P);
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = (1<<18) + 5, P = 998244353, qr2 = 116195171;
inline int read() {
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
ll Pow(ll a, int b) {
ll ans = 1;
for(; b; b >>= 1, a = a * a %P)
if(b & 1) ans = ans * a %P;
return ans;
}
namespace fft {
int rev[N];
void dft(int *a, int n, int flag) {
int k = 0; while((1<<k) < n) k++;
for(int i=0; i<n; i++) {
rev[i] = (rev[i>>1]>>1) | ((i&1)<<(k-1));
if(i < rev[i]) swap(a[i], a[rev[i]]);
}
for(int l=2; l<=n; l<<=1) {
int m = l>>1;
ll wn = Pow(3, flag == 1 ? (P-1)/l : P-1-(P-1)/l);
for(int *p = a; p != a+n; p += l)
for(int k=0, w=1; k<m; k++, w = w*wn%P) {
int t = (ll) w * p[k+m] %P;
p[k+m] = (p[k] - t + P) %P;
p[k] = (p[k] + t) %P;
}
}
if(flag == -1) {
ll inv = Pow(n, P-2);
for(int i=0; i<n; i++) a[i] = a[i] * inv %P;
}
}
int t[N];
void inverse(int *a, int *b, int l) {
if(l == 1) {b[0] = Pow(a[0], P-2); return;}
inverse(a, b, l>>1);
int n = l<<1;
for(int i=0; i<l; i++) t[i] = a[i], t[i+l] = 0;
dft(t, n, 1); dft(b, n, 1);
for(int i=0; i<n; i++) b[i] = (ll) b[i] * (2 - (ll) t[i] * b[i] %P + P) %P;
dft(b, n, -1); for(int i=l; i<n; i++) b[i] = 0;
}
}
int n, a[N], b[N], len;
ll inv[N], fac[N], facInv[N];
int main() {
//freopen("in", "r", stdin);
freopen("dag_count.in", "r", stdin);
freopen("dag_count.out", "w", stdout);
n = read();
inv[1] = fac[0] = facInv[0] = 1;
for(int i=1; i<=n; i++) {
if(i != 1) inv[i] = (P - P/i) * inv[P%i] %P;
fac[i] = fac[i-1] * i %P;
facInv[i] = facInv[i-1] * inv[i] %P;
}
for(int i=1; i<=n; i++) {
int t = facInv[i] * Pow(Pow(qr2, (ll) i * i %(P-1)), P-2) %P;
if(i&1) b[i] = P - t; else b[i] = t;
}
b[0] = (b[0] + 1) %P;
len = 1; while(len <= n) len <<= 1;
fft::inverse(b, a, len);
int ans = (ll) a[n] * fac[n] %P * Pow(qr2, (ll) n * n %(P-1)) %P;
printf("%d\n", ans);
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 5005, P = 10007;
inline int read() {
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n, g[N], f[N];
int inv[N], fac[N], facInv[N], mi[P+5];
inline int C(int n, int m) {return fac[n] * facInv[m] %P * facInv[n-m] %P;}
int main() {
freopen("DAGIII.in", "r", stdin);
freopen("DAGIII.out", "w", stdout);
//freopen("in", "r", stdin);
n = read();
inv[1] = fac[0] = facInv[0] = 1;
for(int i=1; i<=n; i++) {
if(i != 1) inv[i] = (P - P/i) * inv[P%i] %P;
fac[i] = fac[i-1] * i %P;
facInv[i] = facInv[i-1] * inv[i] %P;
}
mi[0] = 1;
for(int i=1; i<=P; i++) mi[i] = mi[i-1] * 2 %P;
g[0] = 1;
for(int i=1; i<=n; i++)
for(int k=1; k<=i; k++)
g[i] = (g[i] + ((k&1) ? 1 : -1) * C(i, k) %P * mi[k * (i-k) % (P-1)] %P * g[i-k] %P) %P;
f[0] = 1;
for(int i=1; i<=n; i++) {
f[i] = g[i];
for(int j=1; j<i; j++) f[i] = (f[i] - (ll) C(i-1, j-1) * f[j] %P * g[i-j]) %P;
if(f[i] < 0) f[i] += P;
}
printf("%d\n", (f[n] + P) %P);
}
有标号DAG计数 [容斥原理 子集反演 组合数学 fft]的更多相关文章
- P6295 有标号 DAG 计数
P6295 有标号 DAG 计数 题意 求 \(n\) 个点有标号弱联通 DAG 数量. 推导 设 \(f_i\) 表示 \(i\) 个点有标号 DAG 数量(不保证弱联通),有: \[f(i)=\s ...
- 有标号DAG计数(生成函数)
有标号DAG计数(生成函数) luogu 题解时间 首先考虑暴力,很容易得出 $ f[ i ] = \sum\limits_{ j = 1 }^{ i } ( -1 )^{ j - 1 } \bino ...
- Comet Contest#11 F arewell(DAG计数+FWT子集卷积)
传送门. 题解: 4月YY集训时做过DAG计数,和这个基本上是一样的,但是当时好像直接暴力子集卷积,不然我省选时不至于不会,这个就多了个边不选的概率和子集卷积. DAG计数是个套路来的,利用的是DAG ...
- 洛谷 P6295 - 有标号 DAG 计数(生成函数+容斥+NTT)
洛谷题面传送门 看到图计数的题就条件反射地认为是不可做题并点开了题解--实际上这题以我现在的水平还是有可能能独立解决的( 首先连通这个条件有点棘手,我们尝试把它去掉.考虑这题的套路,我们设 \(f_n ...
- 题解 有标号DAG计数
题目传送门 题目大意 给出\(n\),求出对于任意\(t\in[1,n]\),点数为\(t\)的弱联通\(\texttt{DAG}\)个数.答案对\(998244353\)取模. \(n\le 10^ ...
- P6295-有标号 DAG 计数【多项式求逆,多项式ln】
正题 题目链接:https://www.luogu.com.cn/problem/P6295 题目大意 求所有\(n\)个点的弱联通\(DAG\)数量. \(1\leq n\leq 10^5\) 解题 ...
- 【题解】有标号的DAG计数1
[HZOI 2015] 有标号的DAG计数 I 设\(f_i\)为\(i\)个点时的DAG图,(不必联通) 考虑如何转移,由于一个DAG必然有至少一个出度为\(0\)的点,所以我们钦定多少个出度为\( ...
- 有标号的DAG计数(FFT)
有标号的DAG计数系列 有标号的DAG计数I 题意 给定一正整数\(n\),对\(n\)个点有标号的有向无环图(可以不连通)进行计数,输出答案\(mod \ 10007\)的结果.\(n\le 500 ...
- cogs 2355. [HZOI 2015] 有标号的DAG计数 II
题目分析 来自2013年王迪的论文<浅谈容斥原理> 设\(f_{n,S}\)表示n个节点,入度为0的点集恰好为S的方案数. 设\(g_{n,S}\)表示n个节点,入度为0的点集至少为S的方 ...
随机推荐
- Open-air shopping malls(二分半径,两元交面积)
http://acm.hdu.edu.cn/showproblem.php?pid=3264 Open-air shopping malls Time Limit: 2000/1000 MS (Jav ...
- javascript 之原型、原型链-14
原型 原型是一个对象,每个函数对象(在javascript 之对象中说过函数也是对象 )都有一个属性(prototype)指向这个对象--原型对象,这个对象的作用是让所有对象实例共享原型对象中的属性. ...
- [20160711][neven代码移植Windows]
相关说明 neven代码用于人脸检测,该代码是从Android源代码中抽取出来的,可以在Linux系统下通过make命令直接进行编译,并且可以通过代码中opencv目录下的测试文件进行测试. 移植环境 ...
- RegExp对象的三个方法
RegExp 对象有 3 个方法:test().exec() 以及 compile(). test( ) test()方法检索字符串中的指定值.返回值是true或false. 例子: 因为字符 ...
- 基于 fireasy 构建的 asp.net core 示例
最近花时间弄了一个关于fireasy使用的demo,已放到 github 上供大家研究,https://github.com/faib920/zero 该 demo 演示了如何使用 fireasy 创 ...
- Arrays类详解
数组是数据结构中最简单的一种类型.在平常的使用上也比较多见.今天就来总结一下数组在使用过程中的一些心得 1.java中包装数组的一些基本用法的抽象类 java.util.Arrays.这个类中包含操 ...
- 什么是A记录 域名
A (Address) 记录是用来指定主机名(或域名)对应的IP地址记录.用户可以将该域名下的网站服务器指向到自己的web server,FTP server等上面.同时也可以设置域名的子域名. 通俗 ...
- Linux - ubuntu 16 打开SSH服务
ubuntu 16 打开SSH服务 1.查看是否启动进程 roott@jiqing-virtual-machine:~# ps -ef | grep sshd root 3477 1 0 18:36 ...
- hashCode方法和equals方法比较
为什么用HashCode比较比用equals方法比较要快呢?我们要想比较hashCode与equals的性能,得先了解HashCode是什么. HashCode HashCode是jdk根据对象的地址 ...
- Centos7-两台Centos机器间复制文件
我又两台Centos机器,一台192.168.1.1:另一台192.168.1.2 现在在将192.168.1.1上的一个文件复制到192.168.1.2.登陆到192.168.1.1然后运行命令 命 ...