题解

前置芝士 :矩阵树定理

本题是一道计数题,有两个要求:

  1. 建造的公路构成一颗生成树

  2. 每条公路由不同的公司建造,每条公路与一个公司一一映射

那么看到这两个要求后,我们很容易想到第一个条件用矩阵树定理,那么对于第二个条件,我们就很容易想到容斥原理。

先不考虑第二个条件,把所有边都加进去(没有自环),这是我们用矩阵树原理算出来的结果不仅有 \(n-1\) 个公司建造的方案,也包括了 \((n-2)...1\) 个公司建造的方案。

此时,我们需要减去 \(n-2\) 个公司建造的方案,那么这里我们就把其中一个公司去掉,再进行计算,注意这里去掉一个公司有 \(n-1\) 种方案。

但是我们会发现 \(n-3\) 个公司建造的方案被重复减去了,所以我们需要加回来,至此,就是一个纯的容斥了。

对于删去不同的公司,计算不同的方案,我们可以用二进制压一下 \(n-1\) ,二进制每一位 \(1\) 代表选取这一位代表的公司。

而在求解行列式的过程中,我们可以直接利用逆元进行求解,也可以辗转相除。

所以最后前一种复杂度为 \(\mathcal O(2^{n-1}((n-1)^3+(n-1)log(1e9+7)))\) 后一种的复杂度为 \(\mathcal O(2^{n-1}(n-1)^3logn)\) 。

Code

逆元 \(AC \kern 0.4em CODE:\)

#include<bits/stdc++.h>
#define ri register signed
#define p(i) ++i
using namespace std;
namespace IO{
char buf[1<<21],*p1=buf,*p2=buf;
#define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++
inline int read() {
ri x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
return x*f;
}
}
using IO::read;
namespace nanfeng{
#define lm(x) (1<<x)
#define lowbit(x) (x&-(x))
#define cmax(x,y) ((x)>(y)?(x):(y))
#define cmin(x,y) ((x)>(y)?(y):(x))
#define FI FILE *IN
#define FO FILE *OUT
typedef long long ll;
static const int N=20,MOD=1e9+7;
int num[N],u[N][N*N],v[N][N*N],G[N][N],siz[lm(16)+7],lg[lm(16)+7]={-1},n,cnt,st,ans;
inline void add(int u,int v) {p(G[u][v]),p(G[v][u]);}
inline int fpow(int x,int y) {
int res=1;
while(y) {
if (y&1) res=1ll*res*x%MOD;
x=1ll*x*x%MOD;y>>=1;
}
return res;
}
inline int Gauss() {
int res=1,tr=0;
for (ri i(1);i<=cnt;p(i)) {
for (ri j(i+1);j<=cnt;p(j)) if (G[j][i]) {swap(G[i],G[j]);tr^=1;break;}
int inv=fpow(G[i][i],MOD-2);
for (ri j(i+1);j<=cnt;p(j)) {
int tmp=1ll*inv*G[j][i]%MOD;
for (ri k(i+1);k<=cnt;p(k)) G[j][k]=(G[j][k]-1ll*G[i][k]*tmp%MOD+MOD)%MOD;
}
if (!G[i][i]) return 0;
res=1ll*res*G[i][i]%MOD;
}
return tr?-res:res;
}
inline int main() {
// FI=freopen("nanfeng.in","r",stdin);
// FO=freopen("nanfeng.out","w",stdout);
n=read(),cnt=n-1;st=(1<<n-1)-1;
for (ri i(1);i<=st;p(i)) siz[i]=siz[i>>1]+(i&1),lg[i]=lg[i>>1]+1;
for (ri i(1);i<n;p(i)) {
num[i]=read();
for (ri j(1);j<=num[i];p(j)) u[i][j]=read(),v[i][j]=read();
}
for (ri i(1);i<=st;p(i)) {
int low=i;
memset(G,0,sizeof(G));
while(low) {
int id=lg[lowbit(low)]+1;
for (ri j(1);j<=num[id];p(j)) add(u[id][j],v[id][j]);
low-=lowbit(low);
}
for (ri j(1);j<=n;p(j)) {
for (ri k(1);k<=n;p(k)) if (j^k) G[j][j]+=G[j][k],G[j][k]=-G[j][k];
}
// for (ri j(1);j<=n;p(j)) {
// for (ri k(1);k<=n;p(k)) printf("%d ",G[j][k]);
// puts("");
// }
int tmp=(Gauss()+MOD)%MOD;
// printf("state=%d tmp=%d\n",i,tmp);
ans=((ll)ans+MOD+((n-siz[i])&1?tmp:-tmp))%MOD;
}
printf("%d\n",ans);
return 0;
}
}
int main() {return nanfeng::main();}
Code

辗转相除法 \(AC \kern 0.4em CODE:\)

#include<bits/stdc++.h>
#define ri register signed
#define p(i) ++i
using namespace std;
namespace IO{
char buf[1<<21],*p1=buf,*p2=buf;
#define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++
inline int read() {
ri x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
return x*f;
}
}
using IO::read;
namespace nanfeng{
#define lm(x) (1<<x)
#define lowbit(x) (x&-(x))
#define cmax(x,y) ((x)>(y)?(x):(y))
#define cmin(x,y) ((x)>(y)?(y):(x))
#define FI FILE *IN
#define FO FILE *OUT
typedef long long ll;
static const int N=20,MOD=1e9+7;
int num[N],u[N][N*N],v[N][N*N],G[N][N],siz[lm(16)+7],lg[lm(16)+7]={-1},n,cnt,st,ans;
inline void add(int u,int v) {p(G[u][v]),p(G[v][u]);}
inline int Gauss() {
int res=1,tr=0;
for (ri i(1);i<=cnt;p(i)) {
for (ri j(i+1);j<=cnt;p(j)) {
while(G[j][i]) {
int k=G[i][i]/G[j][i];
for (ri l(i);l<=cnt;p(l)) G[i][l]=(G[i][l]-(ll)G[j][l]*k%MOD)%MOD;
swap(G[i],G[j]);tr^=1;
}
}
if (!G[i][i]) return 0;
res=(ll)res*G[i][i]%MOD;
}
return tr?-res:res;
}
inline int main() {
// FI=freopen("nanfeng.in","r",stdin);
// FO=freopen("nanfeng.out","w",stdout);
n=read(),cnt=n-1;st=(1<<n-1)-1;
for (ri i(1);i<=st;p(i)) siz[i]=siz[i>>1]+(i&1),lg[i]=lg[i>>1]+1;
for (ri i(1);i<n;p(i)) {
num[i]=read();
for (ri j(1);j<=num[i];p(j)) u[i][j]=read(),v[i][j]=read();
}
for (ri i(1);i<=st;p(i)) {
int low=i;
memset(G,0,sizeof(G));
while(low) {
int id=lg[lowbit(low)]+1;
for (ri j(1);j<=num[id];p(j)) add(u[id][j],v[id][j]);
low-=lowbit(low);
}
for (ri j(1);j<=n;p(j)) {
for (ri k(1);k<=n;p(k)) if (j^k) G[j][j]+=G[j][k],G[j][k]=-G[j][k];
}
// for (ri j(1);j<=n;p(j)) {
// for (ri k(1);k<=n;p(k)) printf("%d ",G[j][k]);
// puts("");
// }
int tmp=(Gauss()+MOD)%MOD;
// printf("state=%d tmp=%d\n",i,tmp);
ans=((ll)ans+MOD+((n-siz[i])&1?tmp:-tmp))%MOD;
}
printf("%d\n",ans);
return 0;
}
}
int main() {return nanfeng::main();}

题解 P4336 [SHOI2016]黑暗前的幻想乡的更多相关文章

  1. P4336 [SHOI2016]黑暗前的幻想乡

    P4336 [SHOI2016]黑暗前的幻想乡 矩阵树定理(高斯消元+乘法逆元)+容斥 ans=总方案数 -(公司1未参加方案数 ∪ 公司2未参加方案数 ∪ 公司3未参加方案数 ∪ ...... ∪ ...

  2. 洛谷P4336 [SHOI2016]黑暗前的幻想乡 [Matrix-Tree定理,容斥]

    传送门 思路 首先看到生成树计数,想到Matrix-Tree定理. 然而,这题显然是不能Matrix-Tree定理硬上的,因为还有每个公司只能建一条路的限制.这个限制比较恶心,尝试去除它. 怎么除掉它 ...

  3. Luogu P4336 [SHOI2016]黑暗前的幻想乡 矩阵树定理+容斥原理

    真是菜到爆炸....容斥写反(反正第一次写qwq) 题意:$n-1$个公司,每个公司可以连一些边,求每个边让不同公司连的生成树方案数. 矩阵树定理+容斥原理(注意到$n$不是很大) 枚举公司参与与否的 ...

  4. [ZJOI2016]小星星&[SHOI2016]黑暗前的幻想乡(容斥)

    这两道题思路比较像,所以把他们放到一块. [ZJOI2016]小星星 题目描述 小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品.她有n颗小星星,用m条彩色的细线串了起来,每条细线连着两颗小星星. ...

  5. 【BZOJ4596】[Shoi2016]黑暗前的幻想乡 容斥+矩阵树定理

    [BZOJ4596][Shoi2016]黑暗前的幻想乡 Description 幽香上台以后,第一项措施就是要修建幻想乡的公路.幻想乡有 N 个城市,之间原来没有任何路.幽香向选民承诺要减税,所以她打 ...

  6. bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥)

    bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥) bzoj Luogu 题解时间 看一看数据范围,求生成树个数毫无疑问直接上矩阵树定理. 但是要求每条边都 ...

  7. bzoj 4596 [Shoi2016]黑暗前的幻想乡 矩阵树定理+容斥

    4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 559  Solved: 325[Submit][Sta ...

  8. bzoj4596[Shoi2016]黑暗前的幻想乡 Matrix定理+容斥原理

    4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 464  Solved: 264[Submit][Sta ...

  9. 【BZOJ 4596】 4596: [Shoi2016]黑暗前的幻想乡 (容斥原理+矩阵树定理)

    4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 324  Solved: 187 Description ...

随机推荐

  1. MyBatis框架中的条件查询!关键字exists用法的详细解析

    exists用法 exists: 如果括号内子查询语句返回结果不为空,说明where条件成立,就会执行主SQL语句 如果括号内子查询语句返回结果为空,说明where条件不成立,就不会执行主SQL语句 ...

  2. ESP32构建系统(CMake版)

    ESP32 芯片是一款 2.4 GHz Wi-Fi 和蓝牙双模芯片,内置 1 或 2 个 32 位处理器,运算能力最高可达 600 DMIPS. ESP-IDF 即乐鑫物联网开发框架,可为在 Wind ...

  3. create-react-app 入门学习

    全局安装 create-react-app npm install create-react-app -g 创建项目 在全局安装了create-react-app 后 创建项目,如果按照下面的第一种办 ...

  4. 「SPOJ 3105」Power Modulo Inverted

    「SPOJ 3105」Power Modulo Inverted 传送门 题目大意: 求关于 \(x\) 的方程 \[a^x \equiv b \;(\mathrm{mod}\; p) \] 的最小自 ...

  5. c++ 进制转换源代码

    #include<stdio.h> int main() { char ku[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C ...

  6. 团队开发day09

    web端的数据成功存储,但是和android端的数据获取到的数据不适配, 进行数据之间的适配. 遇到问题:在安卓中glide不能解析png图片,于是修改二进制流的转化,将png先转化为jpg 再存入到 ...

  7. 【论文阅读】套娃之 Blog for DMP Dynamic Movement Primitives

    前言:原笔记Notion链接:https://www.notion.so/Blog-for-DMP-d34e83c05eb944ba989fc8bf9b0c0f7b 如有格式显示问题等请点击此处查看 ...

  8. 前端开发入门到进阶第三集【sublime 的package control ——install package报错】

    参考:https://www.cnblogs.com/ae6623/p/5338049.html,解决2帮我解决问题. 解决Sublime包管理package control 报错 There are ...

  9. python -- 程序异常与调试(程序调试)

    一.程序调试 A.使用assert语句检测程序代码中的错误. assert 表达式[, 参数] 如果表达式为True,则继续往下运行:如果为False,则抛出一个AssertionError异常,并且 ...

  10. VirtualBox 修改Android x86虚拟机的分辨率

    首先说明一下,本人使用的是Windows下的VirtualBox,android x86使用的是9.0-r2版本 一.查看virtualbox中已有的分辨率 启动虚拟机后,连续按两次E键,进入下面页面 ...