题目背景

$Billions\ of\ lighthouses...stuck\ at\ the\ far\ end\ of\ the\ sky.$


题目描述

平面有$n$个灯塔,初始时两两之间可以相互交流;但由于地形原因,有$m$对灯塔之间无法进行直接的交流。也就是一张完全图缺少了$m$条边。
    $River$想把这$n$个灯塔连成一个环,使得$n$个等他都在环上,并且环上相邻的两个灯塔能进行直接交流。$River$想知道这样做的方案数是多少,两种方案被认为是不同的,当且仅当有两个灯塔$u,v$,他们在一种方案中在环上相邻,而在另一种方案中相反。
    答案可能很大,你只需要输出对${10}^9+7$取模的结果。


输入格式

第一行两个整数$n,m$。
接下来$m$行,每行描述一条缺少的边。


输出格式

一行一个整数表示答案。


样例

样例输入1:

4 1
1 2

样例输出1:

1

样例输入2:

10 3
1 9
3 8
2 7

样例输出2:

87840


数据范围与提示

样例$1$解释:

唯一的方案是(1,3,2,4)依次连成环。

数据范围:

对于所有数据,有$3\leqslant n\leqslant {10}^7,0\leqslant m\leqslant \min(20,\frac{n(n-1)}{2})$。输入的边中没有重边。


题解

题目实际上球的就是哈密顿回路的数量。由于$m$很小,考虑容斥。
    枚举删除的边的某个子集$S$,设$f_S$表示有多少条哈密顿回路至少包含$S$集合中的边,答案就是$\sum_S(-1)^{|S|}\times f_S$。
    怎么算$f_S$呢?首先判掉$f_S=0$的情况,这包含以下两种情况:
        $\alpha.$仅考虑$S$中的边时,某个点的度数大于$2$。
        $\beta.$出现了环,并且这个环的大小不为$n$。
    特判掉$S$本身就是一个哈密顿回路的情况;假设$S$中的边构成了$k$条链,那么$f_S=s{k-1}\times (n-|S|-1)!$。
    证明:考虑将一条链看成一个点,那么总共有$n-|S|$个点,其环排列方案数为$(n-|S|-1)!$;每条链都可以翻转,因此乘上$2^k$;又由于一条哈密顿回路对应了两个环排列(正反两个方向),还要除以$2$。

时间复杂度:$\Theta(2^m\times m)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,m;
long long jc[10000001];
int fa[10000001];
pair<int,int> pos[10000001];
int tot;
long long ans;
int cnt[10000001],vis[10000001],g[10000001];
int que[10000001];
map<pair<int,int>,bool> h;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
long long qpow(long long x,long long y)
{long long res=1;while(y){if(y&1)res=res*x%1000000007;x=x*x%1000000007;y>>=1;}return res;}
long long solve(int x)
{
int sum=0,k=que[0]=0;
bool flag=0;
for(int i=1;i<=tot;i++)
if((x>>(i-1))&1)
{
que[++que[0]]=i;
fa[pos[i].first]=pos[i].first;
fa[pos[i].second]=pos[i].second;
vis[pos[i].first]=vis[pos[i].second]=g[pos[i].first]=g[pos[i].second]=0;
}
for(int i=1;i<=que[0];i++)
{
if(!vis[pos[que[i]].first])sum++;
if(!vis[pos[que[i]].second])sum++;
if(vis[pos[que[i]].first]==2||vis[pos[que[i]].second]==2)return 0LL;
vis[pos[que[i]].first]++;
vis[pos[que[i]].second]++;
}
for(int i=1,u,v;i<=que[0];i++)
{
if((u=find(pos[que[i]].first))==(v=find(pos[que[i]].second)))flag=1;
fa[u]=v;
}
for(int i=1,u;i<=que[0];i++)
if(!g[u=find(pos[que[i]].first)]){g[u]=1;k++;}
if(flag&&(cnt[x]!=n||k>1))return 0LL;
long long res=jc[n-sum+k-1]*qpow(2,k)%1000000007*qpow(2,1000000005)%1000000007;
return (cnt[x]&1)?-res:res;
}
int main()
{
jc[0]=1;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(x==y||h[make_pair(x,y)])continue;
h[make_pair(x,y)]=1;
pos[++tot]=make_pair(x,y);
}
for(int i=1;i<=n;i++)jc[i]=1LL*i*jc[i-1]%1000000007;
for(int i=0;i<(1<<tot);i++)cnt[i]=cnt[i>>1]+(i&1);
for(int i=0;i<(1<<tot);i++)ans=(ans+solve(i))%1000000007;
printf("%lld",(ans+1000000007)%1000000007);
return 0;
}

rp++

[CSP-S模拟测试]:Lighthouse(哈密顿回路+容斥)的更多相关文章

  1. [CSP-S模拟测试]:阴阳(容斥+计数+递推)

    题目传送门(内部题16) 输入格式 第一行两个整数$n$和$m$,代表网格的大小.接下来$n$行每行一个长度为$m$的字符串,每个字符若为$W$代表这个格子必须为阳,若为$B$代表必须为阴,若为$?$ ...

  2. [CSP-S模拟测试]:多维网格(组合数学+容斥)

    题目传送门(内部题138) 输入格式 输入数据第一行为两个整数$d,n$. 第二行$d$个非负整数$a_1,a_2,...,a_d$.     接下来$n$行,每行$d$个整数,表示一个坏点的坐标.数 ...

  3. [CSP-S模拟测试]:连连看(图论+容斥)

    题目传送门(内部题74) 输入格式 输入文件$link.in$ 第一行三个整数$n,m,k$,之间用空格隔开,$n,m$表示地图行数和列数,$k$表示每个方块周围相邻的位置(至多有$4$个,至少有$2 ...

  4. [CSP-S模拟测试]:建设城市(city)(组合数学+容斥)

    题目传送门(内部题8) 输入格式 一行三个整数$n,m,k$. 输出格式 一行一个整数表示答案.对$998244353$取模. 样例 样例输入 3 7 3 样例输出 数据范围与提示 对于10%的数据, ...

  5. csp-s模拟测试59(10.4)「Reverse」(set)·「Silhouette」(容斥)

    A. Reverse 菜鸡wwb又不会了..... 可以线段树优化建边,然而不会所以只能set水了 发现对于k和当前反转点固定的节点x确定奇偶性所到达的节点奇偶性是一定的 那么set维护奇偶点,然后每 ...

  6. 【GDOI2016模拟3.16】幂(容斥 + 模型复杂转化)

    [GDOI2016模拟3.16]幂 \(X\in[1,A],Y\in[1,B]\),问:\(x^y\)的不用取值个数. \(A,B\)都是\(10^9\)级别. 然后我们开搞. 首先,假设一个合法的\ ...

  7. hdu 6169 Senior PanⅡ Miller_Rabin素数测试+容斥

    Senior PanⅡ Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others) Pr ...

  8. 2018.10.30 NOIP模拟 字胡串(单调栈+容斥)

    传送门 对于每个点,用单调栈求出它左右第一个比他大的位置. 然后对每个点O(logai)O(log_{a_i})O(logai​​)求出第一个拥有跟它不同二进制位的位置. 然后容斥一下就行了. 代码

  9. NOIp模拟赛 巨神兵(状压DP 容斥)

    \(Description\) 给定\(n\)个点\(m\)条边的有向图,求有多少个边集的子集,构成的图没有环. \(n\leq17\). \(Solution\) 问题也等价于,用不同的边集构造DA ...

随机推荐

  1. cts-verifier测试流程

    测试目的: cts的补充测试,可以理解为没法自动化的cts测试,这个是人工测试. 测试前提: 1.发货user版本 2.selinux:Enable 5.外网环境 设备需求: 2个待测设备:1个手机或 ...

  2. 线程局部存储tls的使用

    线程局部存储(Thread Local Storage,TLS)主要用于在多线程中,存储和维护一些线程相关的数据,存储的数据会被关联到当前线程中去,并不需要锁来维护.. 因此也没有多线程间资源竞争问题 ...

  3. U盘修复教程--使用量产工具和芯片检测工具

    U盘修复教程(金士顿DT101) 本教程使用量产工具(量产可理解为强制恢复出厂设置),量产有风险(U盘彻底报废),根据教程所造成所有损失均与本文作者无关. ·第一步:下载ChipGenius_v4_0 ...

  4. Pikachu漏洞练习平台实验——RCE(五)

    1.概述 RCE(Remote Command/Code Execute) 给攻击者向后台服务器远程注入操作系统命令或者代码,从而控制后台系统. 远程系统命令执行一般出现这种漏洞,是因为应用系统从设计 ...

  5. ssh公私钥免密登陆

    简介ssh Secure Shell(简写SSH) 为一项建立在应用层和传输层基础上的安全协议,专门为远程登录会话和其他网络服务提供安全性的协议. SSH安全机制分为两种,一种是基于口令的安全认证,一 ...

  6. Action 分离

    原文地址:http://www.cnblogs.com/giggle/p/5380832.html?utm_source=tuicool&utm_medium=referral 本处摘要备注. ...

  7. ubuntu18.04 安装 jdk

    1.当前路径(如果不想下载到当前路径,可以先cd到指定路径,再开始下载),官网下载JDK文件jdk-8u121-linux-x64.tar.gz $ wget https://download.ora ...

  8. Codeforces - 1191C - Tokitsukaze and Discard Items - 模拟

    https://codeforces.com/contest/1191/problem/C 一开始想象了一下,既然每次删除都是往前面靠,那么好像就是页数*页容量+空位数=最多容纳到的坐标. 至于为什么 ...

  9. ls命令输出文件的绝对路径

    find $PWD | xargs ls -ld 再结合 grep 筛选

  10. Git --05 Gitlab使用

    目录 Gitlab使用 01. 外观配置 02. Gitlab汉化配置 03. 注册限制 04. 创建用户及组 05. 创建用户 06. 把用户添加到组 07. 创建项目 08. 推送代码到Gitla ...