@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. Linux & CentOS & RHEL

    1.修改centos7的系统编码:https://blog.csdn.net/violet_echo_0908/article/details/58063555 2.windows 环境下使用ultr ...

  2. OCR Tesseract 识别报 empty page解决办法

    图片分辨率太低导致 周边加空白 然后重新操作,就行了

  3. 用navicat把MYSQL一张表的数据批追加到另外一张表

    1. 表结构完全一样 insert into 表1 select * from 表22. 表结构不一样(这种情况下得指定列名) insert into 表1 (列名1,列名2,列名3) select ...

  4. 使用netbeans 搭建maven工程 整合spring springmvc框架

    使用netbeans7.4 自带的tomcat7.0 所以jdk选择7.xx 然后等待生成空的工程,会缺一些文件夹,和文件,后续需要的时候补齐 然后修改pom.xml添加引用,直接覆盖dependen ...

  5. ifconfig配置IP地址和子网掩码

    ifconfig eth0 192.168.2.10 ifconfig eth0 192.168.2.10 netmask 255.255.255.0

  6. 微信小程序--每个独立的page的page.json只能修改window属性

    app.json 配置项列表 app.json文件用来对微信小程序进行全局配置,决定页面文件的路径.窗口表现.设置网络超时时间.设置多 tab 等. window配置 用于设置小程序的状态栏.导航条. ...

  7. (二)SpringBoot功能

    web开发 spring boot web开发非常的简单,其中包括常用的json输出.filters.property.log等 json 接口开发 在以前的spring 开发的时候需要我们提供jso ...

  8. channel补充

    网易: package main import ( "fmt" ) func main() { var c chan int fmt.Printf("c=%v\n&quo ...

  9. 【JZOJ5068】【GDSOI2017第二轮模拟】树 动态规划+prufer序列

    题面 有n个点,它们从1到n进行标号,第i个点的限制为度数不能超过A[i]. 现在对于每个s (1 <= s <= n),问从这n个点中选出一些点组成大小为s的有标号无根树的方案数. 10 ...

  10. 去掉CSS赘余代码,CSS可以更简洁

    本篇文章适合css新手学习,对于已经掌握了css的朋友们也可以通过本片文章来复习知识. 作者通过实践,认为在有些情况下css的代码是可以更加简洁的,多数情况下是因为新手对于一些具有多属性的元素代码不能 ...