题目背景

$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. CET-6 分频周计划生词筛选(番外篇:百词斩)

    点我阅读 番外-百词斩 2016.09.18 12:00pm transverse counterpart accessory cult gorgeous sediment assimilate st ...

  2. oracle三大范式

    范式: 设计数据库定义的一个规则, 三大范式, 灵活运用, 人的思想是活的 一范式 1, 不存在冗余数据 同一个表中的记录不能有重复----所以主键(必须有) 2, 每个字段必须是不可再分的信息(列不 ...

  3. SQL根据日期计算当月有多少天(转)

    原文链接:https://blog.csdn.net/dobear_0922/article/details/2393235 --1.删除测试用表IF OBJECT_ID(N'Test', N'U') ...

  4. hive 取排行第二的工资

    CREATE TABLE employee( id INT , salary INT ); INSERT INTO employee , UNION ALL , UNION ALL ,; SELECT ...

  5. 关于URL和URI的最简单理解

    以下面网址为例: http://www.sina.com/news/1.html 那么,http://www.sina.com/news/1.html就表示URL,用于标识互联网中的某一资源:/new ...

  6. google+ sign in and get the oauth token 转摘:https://gist.github.com/ianbarber/5170508

    package com.example.anothersignintest;   import java.io.IOException;   import com.google.android.gms ...

  7. tensorflow用dropout解决over fitting

    在机器学习中可能会存在过拟合的问题,表现为在训练集上表现很好,但在测试集中表现不如训练集中的那么好. 图中黑色曲线是正常模型,绿色曲线就是overfitting模型.尽管绿色曲线很精确的区分了所有的训 ...

  8. Java实现二分法(折半)查找数组中的元素

    二分查找 算法思想:又叫折半查找,要求待查找的序列有序.每次取中间位置的值与待查关键字比较,如果中间位置的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,则在后半部分 ...

  9. golang指针函数

    func main() { a := models.SmsVerify{} a.Id = 100 fmt.Println(a.Id) // 100 test111(a) fmt.Println(a.I ...

  10. Error- spark streaming 打包将全部依赖打进去Invalid signature file digest for Manifest main attributes

    spark streaming 打包将全部依赖打进去,运行jar包报错:如下 Exception in thread "main" java.lang.SecurityExcept ...