@description@

已知 n 个点,点 i 与点 j 有 C(i, j) 种不同的连边方式(这个不是组合数!)。

求最终可能的不同连通图个数。

input

第一行一个正整数 n. n <= 20.

接下来 n − 1 行, 第 i 行 n − i 个正整数, 第 j 个数表示 C(i, i+j) .

output

一行一个整数, 表示答案对 10^9 + 7 取模的结果.

sample input

3

2 3

4

sample output

50

@solution@

@part - 1@

数据范围明显暗示状压。

定义 \(f(s)\) 表示集合状态为 s 的连通图个数,我们考虑怎么 dp 去求解 \(f(s)\)。

一个常用的操作:容斥。我们再定义 \(g(s)\) 表示集合状态为 s 的管你连不连通的图。则有 dp 转移式(其实也就是容斥):

\[f(s) = g(s) - \sum_{t\subset s}f(t)*g(s-t)
\]

其中为了避免重复计算,我们还要求 s 中的编号最小的点与 t 中的编号最小的点相同。

既然是容斥,放宽了限制,那么 g 也很好求:

\[g(s)=\prod_{i<j且i,j\in s}(C(i,j)+1),g(0)=0
\]

由于涉嫌枚举子集,这是一个 O(3^n) 的 dp。考虑怎么进行优化。

@part - 2@

我们是要求解全集 s 的 dp 值,又要求编号最小的点的 dp 才能转移到全集 s。因此我们所有有用的 dp 值必须包含 1 号点。我们就可以把所有状态右移一位,省去同时包含编号最小的点这一限制。

这样 dp 转移式变为:

\[f'(s) = h(s) - \sum_{t\subset s}f'(t)*g'(s-t)
\]

注意 h 和 g' 是不一样的,前者必须要包含 1 号点,后者不能包含一号点。

长得一幅子集卷积模样。我们用卷积的形式再来写一遍式子:

\[f' = h - f'*g'
\]

基本的代数变形:

\[f' = h*(g'+1)^{-1}
\]

那么我们就是要来求解子集卷积的逆运算。

@part - 3@

怎么求解子集卷积的逆运算呢?我们先来看看子集卷积本身:

\[f(s)=\sum_{t\subset s}g(t)*h(s-t)
\]

我们讨论二进制表示下 1 的个数。令 \(f_i(s)\) 表示二进制表示下 1 的个数为 i 的集合状态为 s 的值,如果 s 二进制表示下 1 的个数不等于 i 则该值为 0。

好。那么我们可以直接用 \(g_i\) 与 \(h_j\) 卷积卷出 \(f_{i+j}\),然后把 \(f_{i+j}\) 中不合法的集合状态再全部赋值为 0。

假如我们先 FWT/FMT 做完正变换,然后卷积式子变为:

\[\hat f_{i}(s) = \sum_{p+q=i}\hat g_p(s)*\hat h_q(s)
\]

这是一个多项式加法卷积。但是我们可以直接暴力 O(n^2) 做(因为 n 只有 20 啊)。

那么我们考虑逆变换。即已知 \(\hat f_i\) 与 \(\hat g_p\) 求 \(\hat h_q\)。

这是一个多项式除法,但是我们还是直接暴力 O(n^2) 做(都说了 n 只有 20 啊)。

首先:\(\hat f_0(s) = \hat g_0(s)*\hat h_0(s)\)。因此可以直接求解出 \(\hat g_0(s)\)。

然后假设我们已经求解出了 \(g_{0...i-1}(s)\)。

根据公式: \(\hat f_i(s) = \hat g_0(s)*\hat h_i(s) + \dots + \hat g_{i-1}(s)h_{1}(s)+\hat g_i(s)*\hat h_0(s)\)。

因此我们可以把 \(\hat g_i(s)*\hat h_0(s)\) 前面那一团东西暴力求解然后移项,再两边作个除法,就可以求解出 \(g_i(s)\)。

@accepted code@

卡过常……因此有些地方可能有点儿丑陋。

#include<cstdio>
#define rg register
const int MOD = int(1E9) + 7;
const int MAXN = 20;
int C[MAXN][MAXN], lg[1<<MAXN], bits[1<<MAXN];
inline int lowbit(int x) {return x & -x;}
inline int pow_mod(int b, int p) {
int ret = 1;
while( p ) {
if( p & 1 ) ret = 1LL*ret*b%MOD;
b = 1LL*b*b%MOD;
p >>= 1;
}
return ret;
}
inline void fwt(int A[], int n, int type) {
for(rg int s=2;s<=n;s<<=1)
for(rg int i=0,t=(s>>1);i<n;i+=s)
for(rg int j=0;j<t;j++) {
int x = A[i+j], y = A[i+j+t];
A[i+j] = x, A[i+j+t] = (y + type*x)%MOD;
}
}
int p[MAXN][1<<MAXN], q[MAXN][1<<MAXN], r[MAXN][1<<MAXN];
int f[1<<MAXN];
int main() {
int n; scanf("%d", &n);
for(rg int i=0;i<n;i++)
for(rg int j=i+1;j<n;j++)
scanf("%d", &C[i][j]), C[j][i] = C[i][j];
for(rg int i=0;i<n;i++)
lg[1<<i] = i;
int t = (1<<n); f[0] = 1;
for(rg int s=1;s<t;s++) {
int x = lg[lowbit(s)]; f[s] = f[s^(1<<x)];
for(rg int j=0;j<n;j++)
if( (1<<j) & (s^(1<<x)) ) f[s] = 1LL*f[s]*(C[x][j] + 1)%MOD;
}
int t1 = (t>>1);
for(rg int s=1;s<t1;s++)
bits[s] = bits[s>>1] + (s & 1);
for(rg int s=0;s<t1;s++) {
p[bits[s]][s] = f[s<<1];
q[bits[s]][s] = f[s<<1|1];
}
for(rg int i=0;i<n;i++)
fwt(p[i], t1, 1), fwt(q[i], t1, 1);
for(rg int s=0;s<t1;s++) {
int inv = pow_mod(p[0][s], MOD-2);
for(rg int i=0;i<n;i++) {
r[i][s] = q[i][s];
for(rg int j=0;j<i;j++)
r[i][s] = (r[i][s] - 1LL*r[j][s]*p[i-j][s]%MOD)%MOD;
r[i][s] = 1LL*r[i][s]*inv%MOD;
}
}
fwt(r[n-1], t1, -1);
printf("%d\n", (r[n-1][t1-1] + MOD)%MOD);
}

@details@

虽然标程给出的是通过 FMT 来做这道题,但是可以看出完全没有这个必要。

另外,本题 3s 时限是不是真的太卡了点……

@雅礼集训01/13 - T1@ union的更多相关文章

  1. @雅礼集训01/10 - T1@ matrix

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个矩阵.求它的所有子矩阵中本质不同的行的个数之和. inp ...

  2. @雅礼集训01/06 - T3@ math

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给出 n, m, x,你需要求出下列式子的值: \[\sum_{ ...

  3. 雅礼集训DAY 6 T1 xmasdag

    感谢gryz的mly大好人再次给我提供了题目和数据. 和昨晚那个题几乎一样,都是x^n最后转化成第二类斯特林数*阶乘*Σ(和路径长度有关的组合数),而因为组合数是可以利用Pascal公式实现O(1)递 ...

  4. [LOJ 6030]「雅礼集训 2017 Day1」矩阵

    [LOJ 6030] 「雅礼集训 2017 Day1」矩阵 题意 给定一个 \(n\times n\) 的 01 矩阵, 每次操作可以将一行转置后赋值给某一列, 问最少几次操作能让矩阵全为 1. 无解 ...

  5. 【loj6034】「雅礼集训 2017 Day2」线段游戏

    #6034. 「雅礼集训 2017 Day2」线段游戏 内存限制:256 MiB 时间限制:1000 ms 标准输入输出 题目类型:传统 评测方式:Special Judge 上传者: 匿名 题目描述 ...

  6. 雅礼集训1-9day爆零记

    雅礼集训1-9day爆零记 先膜一下虐爆我的JEFF巨佬 Day0 我也不知道我要去干嘛,就不想搞文化科 (文化太辣鸡了.jpg) 听李总说可以去看(羡慕)各路大佬谈笑风声,我就报一个名吧,没想到还真 ...

  7. LOJ_6045_「雅礼集训 2017 Day8」价 _最小割

    LOJ_6045_「雅礼集训 2017 Day8」价 _最小割 描述: 有$n$种减肥药,$n$种药材,每种减肥药有一些对应的药材和一个收益. 假设选择吃下$K$种减肥药,那么需要这$K$种减肥药包含 ...

  8. 雅礼集训【Day6-1】字符串

    雅礼集训[Day6-1]字符串 假设我们有串\(a\),我们设\(a'\)为\(a\)翻转后按为取反过后的串. 我们只考虑前一半的,长为\(m\)的串.如果前半截匹配了\(a\)或者\(a'\),则\ ...

  9. 「雅礼集训 2017 Day7」事情的相似度

    「雅礼集训 2017 Day7」事情的相似度 题目链接 我们先将字符串建后缀自动机.然后对于两个前缀\([1,i]\),\([1,j]\),他们的最长公共后缀长度就是他们在\(fail\)树上对应节点 ...

随机推荐

  1. rpmnew和rpmsave怎么来的

    RPM spec文件有个名为 %config 的宏,它可以标识配置文件,这样在升级时用户对配置文件做过的修改就不会丢失.没有它,用户千辛万苦修改过的配置文件会在升级过程中被覆盖. %config也可以 ...

  2. PHPStorm 批量选择,多光标同时编辑相同的内容

    一直按Alt+J

  3. 学习JDK1.8集合源码之--Vector

    1. Vector简介 Vector是JDK1.0版本就推出的一个类,和ArrayList一样,继承自AbstractList,实现了List.RandomAccess.Cloneable.java. ...

  4. SQL优化神器 - Tosska SQL Tuning Expert Pro for Oracle

    SQL Tuning Expert Pro for Oracle 是Tosska 公司推出的划时代SQL优化工具.它可以帮助SQL开发人员和DBA: 找到最快的等价SQL: 调整执行计划: 管理SQL ...

  5. [运维]ESXI系统的安装 标签: 虚拟机运维vmware服务器虚拟化 2017-05-05 09:24 496人阅读 评论(15)

    上篇博客说到了VMware vSphere,那么接下来就讲一下我们如何将之投入使用.vsphere的虚拟机管理程序就是esxi. 什么是ESXI? vSphere产品套件的核心产品是虚拟机管理程序,作 ...

  6. WordPress使用自定义文章类型实现任意模板的方法和怎么做邮件回复

    主要就是使用了register_post_type 函数. 1.创建插件目录 新建一个文件夹用来存放插件文件,这里我就命名这个文件夹为myMood 2.创php代码文件 在刚才创建的文件夹里面新建一个 ...

  7. JavaScript--放大镜

    上例图: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF- ...

  8. 封装函数通过输入(元素,属性,目标值)改变div样式

    ## 假设一个div样式如下```html<!DOCTYPE html><html lang="en"> <head> <meta cha ...

  9. 一.JDBC学习入门

    一.JDBC相关概念介绍 1.1.数据库驱动 这里的驱动的概念和平时听到的那种驱动的概念是一样的,比如平时购买的声卡,网卡直接插到计算机上面是不能用的,必须要安装相应的驱动程序之后才能够使用声卡和网卡 ...

  10. 学习python所需要了解的一些基础计算机知识汇总

    1)编程语言 语言是一个物体与另一个物体交流的介质,而编程语言就是程序员与计算机沟通的介质,人使用编程语言的目的就是控制计算机为人服务. 例如,用户使用用python语言编写的应用程序通过操作系统向C ...