题解 P4336 [SHOI2016]黑暗前的幻想乡
题解
前置芝士 :矩阵树定理
本题是一道计数题,有两个要求:
建造的公路构成一颗生成树
每条公路由不同的公司建造,每条公路与一个公司一一映射
那么看到这两个要求后,我们很容易想到第一个条件用矩阵树定理,那么对于第二个条件,我们就很容易想到容斥原理。
先不考虑第二个条件,把所有边都加进去(没有自环),这是我们用矩阵树原理算出来的结果不仅有 \(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]黑暗前的幻想乡的更多相关文章
- P4336 [SHOI2016]黑暗前的幻想乡
P4336 [SHOI2016]黑暗前的幻想乡 矩阵树定理(高斯消元+乘法逆元)+容斥 ans=总方案数 -(公司1未参加方案数 ∪ 公司2未参加方案数 ∪ 公司3未参加方案数 ∪ ...... ∪ ...
- 洛谷P4336 [SHOI2016]黑暗前的幻想乡 [Matrix-Tree定理,容斥]
传送门 思路 首先看到生成树计数,想到Matrix-Tree定理. 然而,这题显然是不能Matrix-Tree定理硬上的,因为还有每个公司只能建一条路的限制.这个限制比较恶心,尝试去除它. 怎么除掉它 ...
- Luogu P4336 [SHOI2016]黑暗前的幻想乡 矩阵树定理+容斥原理
真是菜到爆炸....容斥写反(反正第一次写qwq) 题意:$n-1$个公司,每个公司可以连一些边,求每个边让不同公司连的生成树方案数. 矩阵树定理+容斥原理(注意到$n$不是很大) 枚举公司参与与否的 ...
- [ZJOI2016]小星星&[SHOI2016]黑暗前的幻想乡(容斥)
这两道题思路比较像,所以把他们放到一块. [ZJOI2016]小星星 题目描述 小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品.她有n颗小星星,用m条彩色的细线串了起来,每条细线连着两颗小星星. ...
- 【BZOJ4596】[Shoi2016]黑暗前的幻想乡 容斥+矩阵树定理
[BZOJ4596][Shoi2016]黑暗前的幻想乡 Description 幽香上台以后,第一项措施就是要修建幻想乡的公路.幻想乡有 N 个城市,之间原来没有任何路.幽香向选民承诺要减税,所以她打 ...
- bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥)
bzoj4596/luoguP4336 [SHOI2016]黑暗前的幻想乡(矩阵树定理,容斥) bzoj Luogu 题解时间 看一看数据范围,求生成树个数毫无疑问直接上矩阵树定理. 但是要求每条边都 ...
- bzoj 4596 [Shoi2016]黑暗前的幻想乡 矩阵树定理+容斥
4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 559 Solved: 325[Submit][Sta ...
- bzoj4596[Shoi2016]黑暗前的幻想乡 Matrix定理+容斥原理
4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 464 Solved: 264[Submit][Sta ...
- 【BZOJ 4596】 4596: [Shoi2016]黑暗前的幻想乡 (容斥原理+矩阵树定理)
4596: [Shoi2016]黑暗前的幻想乡 Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 324 Solved: 187 Description ...
随机推荐
- ROS2学习之旅(2)——配置ROS2环境
目录 1.source一下setup文件 2.自动source 3.自动进入工作区(不常用) 4.检查环境变量是否设置成功 5.总结 ROS2依赖于使用shell(终端)环境组合工作空间的概念.工作空 ...
- 「CF986F」 Oppa Funcan Style Remastered
「CF986F」 Oppa Funcan Style Remastered Link 首先发现分解成若干个 \(k\) 的因数很蠢,事实上每个因数都是由某个质因子的若干倍组成的,所以可以将问题转换为分 ...
- Python之抖音快手代码舞--字符舞
先上效果,视频敬上: 字符舞: 代码舞 源代码: video_2_code_video.py 1 import argparse 2 import os 3 import cv2 4 import s ...
- 前端性能优化实践-gzip
一名优秀的前端工程师必备技能之一就是要会性能监控,并且能相应的进行性能优化.最近,有需求将项目做一些优化,提升用户的体验.看了一下项目并没有开启gzip,于是着手实现gzip压缩,下面就是具体的实践过 ...
- 解决远程连接服务器数据库报错:Host ‘XXXXXX’ is blocked because of many connection errors
参考:https://blog.csdn.net/li_li_lin/article/details/72764683和 https://blog.csdn.net/zaishijizhidian/a ...
- Java 获取、删除Word文本框中的表格
本文介绍如何来获取Word文本框中包含的表格,以及删除表格. 程序测试环境包括: IDEA JDK 1.8.0 Spire.Doc.jar 注:jar导入,可通过创建Maven程序项目,并在pom.x ...
- Oracle19c 如何用rman duplicate 克隆一个数据库。(Backup-Based, achive log)
Oracle19c 如何用rman duplicate 克隆一个数据库.(Backup-Based, achive log) 首先克隆有两种方法,一种是Backup-Based,一种是Active方式 ...
- 第十篇 -- 学习C++宝典2005版
最近看了C++宝典,看时间是2005的,对于里面的程序自己也进行了编写,由于时间过久,可能有些函数的用法发生了改变,自己也对其进行了修改,用VS2017可以编译通过. 前四章学习内容 CPlusPlu ...
- SourceTree 3.1.3版本跳过注册
1.进入文件夹 %LocalAppData%\Atlassian\SourceTree\ 找到SourceTree的目录 2.里面添加一个json文件 accounts.json 内容如下: [{&q ...
- 🔥 LeetCode 热题 HOT 100(81-90)
337. 打家劫舍 III 思路:后序遍历 + 动态规划 推荐题解:树形 dp 入门问题(理解「无后效性」和「后序遍历」) /** * Definition for a binary tree nod ...