洛谷 P3244 / loj 2115 [HNOI2015] 落忆枫音 题解【拓扑排序】【组合】【逆元】
组合计数的一道好题。什么非主流题目
题目背景
(背景冗长请到题目页面查看)
题目描述
不妨假设枫叶上有 \(n\) 个穴位,穴位的编号为 \(1\sim n\)。有若干条有向的脉络连接着这些穴位。穴位和脉络组成一个有向无环图——称之为脉络图(例如图 1),穴位的编号使得穴位 \(1\) 没有从其他穴位连向它的脉络,即穴位 1 只有连出去的脉络;由上面的故事可知,这个有向无环图存在一个树形子图,它是以穴位 \(1\) 为根的包含全部 \(n\) 个穴位的一棵树——称之为脉络树(例如图 2 和图 3 给出的树都是图 1 给出的脉络图的子图);值得注意的是,脉络图中的脉络树方案可能有多种可能性,例如图 2 和图 3 就是图 1 给出的脉络图的两个脉络树方案。
脉络树的形式化定义为:以穴位 \(r\) 为根的脉络树由枫叶上全部 \(n\) 个穴位以及 \(n-1\) 条脉络组成,脉络树里没有环,亦不存在从一个穴位连向自身的脉络,且对于枫叶上的每个穴位 \(s\),都存在一条唯一的包含于脉络树内的脉络路径,使得从穴位 \(r\) 出发沿着这条路径可以到达穴位 \(s\)。
现在向脉络图添加一条与已有脉络不同的脉络(注意:连接 \(2\) 个穴位但方向不同的脉络是不同的脉络,例如从穴位 \(3\) 到 \(4\) 的脉络与从 \(4\) 到 \(3\) 的脉络是不同的脉络,因此,图 1 中不能添加从 \(3\) 到 \(4\) 的脉络,但可添加从 \(4\) 到 \(3\) 的脉络),这条新脉络可以是从一个穴位连向自身的(例如,图 1 中可添加从 \(4\) 到 \(4\) 的脉络)。原脉络图添加这条新脉络后得到的新脉络图可能会出现脉络构成的环。
请你求出添加了这一条脉络之后的新脉络图的以穴位 \(1\) 为根的脉络树方案数。
由于方案可能有太多太多,请输出方案数对 \(1000000007\) 取模得到的结果。
输入格式
输入文件的第一行包含四个整数 \(n\)、\(m\)、\(x\) 和 \(y\),依次代表枫叶上的穴位数、脉络数,以及要添加的脉络是从穴位 \(x\) 连向穴位 \(y\) 的。
接下来 \(m\) 行,每行两个整数,由空格隔开,代表一条脉络。第 \(i\) 行的两个整数为 \(u_i\) 和 \(v_i\),代表第 \(i\) 条脉络是从穴位 \(u_i\) 连向穴位 \(v_i\) 的。
输出格式
输出一行,为添加了从穴位 \(x\) 连向穴位 \(y\) 的脉络后,枫叶上以穴位 \(1\) 为根的脉络树的方案数对 \(1000000007\) 取模得到的结果。
输入输出样例
输入样例:
4 4 4 3
1 2
1 3
2 4
3 2
输出样例:
3
数据范围与约定
对于所有测试数据,\(1\leq n\leq 100000, \ n-1 \leq m \leq \min \left(200000, \frac{n(n - 1)}{2}\right), \ 1 \leq x, y, u_i, v_i \leq n\)。
题解:
首先需要找出一个不需要拓扑排序就能解决不加边时的脉络树数量的方法。
对于每个点 \(u(u\ge 2)\) ,假定它的入度为 \(d_i\) ,则它有 \(d_i\) 个父亲可供选择。我们只需要从上往下看,就可以发现每一层都是互相独立的,因此加边之前脉络树的数量为
\]
此时考虑加边。正常情况下按上面的方式计数,边数为 \(n-1\) 的图的总数 \(sum\) 为
\]
但是实际上不是所有 \(sum\) 种方案都符合题意,由于每个点选择父亲是自由从入边选的,因此可能存在环,此时就不满足”脉络树“的定义了,而且图/树也没有明显分层。
我们考虑所有的 \(sum\) 种方案,从中减掉包含环的那些方案。由于我们加入的边是 \(\left<x,y\right>\),所以一定是与路径 \(y\to x\) 成环。因此我们只需要排除那些包含 \(y\to x\) 的路径的边数为 \(n-1\) 的图就可以了。
注意由于加了新边之后的图只有一个环,因此 \(n-1\) 条边的图也最多只有一个环。
话再说回来,包含 \(y\to x\) 的路径的图我们可以认为这条路径上的所有点(包括 \(x,y\))都被钦定了一个父亲(其中 \(x\) 的父亲认为是 \(y\),因为要成环)。假设用 \(S=\left\{a_i\right\}\) 表示这条路径,那么包含 \(y\to x\) 的图种类数就是 \(\prod_{i\notin S}d_i\)。
而最终的答案就是 \(sum-\sum_{S:y\to x}\prod_{i\notin S}d_i\)。
此时考虑如何求出所有的 \(y\to x\)。可以建立原图的反向边,跑拓扑排序。设定状态 \(f_k\) 表示 \(\sum_{S:k\to x}\prod_{i\notin S}d_i\),每次从原图的边 \(\left<u,v\right>\) 转移时,即钦定了 \(v\) 的入边,所以状态转移方程为
\]
由于我们还要钦定 \(x\) 的入边是 \(y\),因此最终的答案是
\]
时间复杂度为 \(O(n)\) 或 \(O(n\log n)\) (在线求逆元)
Code:
#include<cstdio>
#include<cstring>
#define p 1000000007
int Plus(int x,int y)
{return (x+y>=p)?(x+y-p):(x+y);}
int Mul(int x,int y)
{return 1ll*x*y%p;}
struct edge
{
int n,nxt;
edge(int n,int nxt)
{
this->n=n;
this->nxt=nxt;
}
edge(){}
}e[200000];
int head[100100],ecnt=-1;
void add(int from,int to)
{
e[++ecnt]=edge(to,head[from]);
head[from]=ecnt;
}
int d[100100],in[100100];
//d表示真实入度 in表示拓扑排序中的入度
int q[100100],l=0,r=0;
int f[100100],inv[100100];
int main()
{
memset(head,-1,sizeof(head));
inv[1]=1;
for(int i=2;i<=100000;++i)
inv[i]=Mul(p-p/i,inv[p%i]);
int n,m,x,y,u,v;
scanf("%d%d%d%d",&n,&m,&x,&y);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&u,&v);
add(v,u);
++in[u];
++d[v];
}
f[x]=1;
int sum=1;
for(int i=2;i<=n;++i)
{
if(!in[i])
q[++r]=i;
f[x]=Mul(f[x],d[i]);
sum=Mul(sum,d[i]+(i==y));
}
if(y==1)
{
printf("%d\n",sum);
return 0;
}
while(l<r)
{
int k=q[++l];
for(int i=head[k];~i;i=e[i].nxt)
{
--in[e[i].n];
f[e[i].n]=Plus(f[e[i].n],Mul(f[k],inv[d[k]]));
if(!in[e[i].n])
q[++r]=e[i].n;
}
}
printf("%d\n",Plus(sum,p-Mul(f[y],inv[d[y]])));
return 0;
}
洛谷 P3244 / loj 2115 [HNOI2015] 落忆枫音 题解【拓扑排序】【组合】【逆元】的更多相关文章
- 【题解】 [HNOI2015]落忆枫音 (拓扑排序+dp+容斥原理)
原题戳我 Solution: (部分复制Navi_Aswon博客) 解释博客中的两个小地方: \[\sum_{\left(S是G中y→x的一条路径的点集\right))}\prod_{2≤j≤n,(j ...
- 【bzoj4011】[HNOI2015]落忆枫音 容斥原理+拓扑排序+dp
题目描述 给你一张 $n$ 个点 $m$ 条边的DAG,$1$ 号节点没有入边.再向这个DAG中加入边 $x\to y$ ,求形成的新图中以 $1$ 为根的外向树形图数目模 $10^9+7$ . 输入 ...
- BZOJ4011:[HNOI2015]落忆枫音(DP,拓扑排序)
Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂……我们也 ...
- [luogu3244 HNOI2015] 落忆枫音(容斥原理+拓扑排序)
传送门 Description 给你一张 n 个点 m 条边的DAG,1 号节点没有入边.再向这个DAG中加入边 x→y ,求形成的新图中以 1 为根的外向树形图数 模 10^9+7 . Input ...
- BZOJ 4011: [HNOI2015]落忆枫音 计数 + 拓扑排序
Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出 这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂……我们 ...
- [BZOJ4011][HNOI2015]落忆枫音:拓扑排序+容斥原理
分析 又是一个有故事的题目背景.作为玩过原作的人,看题目背景都快看哭了ToT.强烈安利本境系列,话说SP-time的新作要咕到什么时候啊. 好像扯远了嘛不管了. 一句话题意就是求一个DAG再加上一条有 ...
- BZOJ 4011: [HNOI2015]落忆枫音( dp )
DAG上有个环, 先按DAG计数(所有节点入度的乘积), 然后再减去按拓扑序dp求出的不合法方案数(形成环的方案数). ---------------------------------------- ...
- bzoj4011[HNOI2015]落忆枫音 dp+容斥(?)
4011: [HNOI2015]落忆枫音 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1125 Solved: 603[Submit][Statu ...
- [HNOI2015]落忆枫音 解题报告
[HNOI2015]落忆枫音 设每个点入度是\(d_i\),如果不加边,答案是 \[ \prod_{i=2}^nd_i \] 意思是我们给每个点选一个父亲 然后我们加了一条边,最后如果还这么统计,那么 ...
随机推荐
- 四.python数据类型,语句
Python基础 阅读: 120476 Python是一种计算机编程语言.计算机编程语言和我们日常使用的自然语言有所不同,最大的区别就是,自然语言在不同的语境下有不同的理解,而计算机要根据编程语言执行 ...
- opennebula kvm 创建虚拟机错误
Thu Jul :: : Error executing image transfer script: Error copying localhost.localdomain:/app/openneb ...
- Python学习笔记_Python向Excel写入数据
实验环境 1.OS:Win 10 64位 2.Python 3.7 3.如果没有安装xlwt库,则安装:pip install xlwt 下面是从网上找到的一段代码,网上这段代码,看首行注释行,是在L ...
- Java EE的十三个规范
J2EE想必大家都不陌生吧,貌似现在更流行将其称作JavaEE,不管名字怎么变,核心和思想是没有变的.学习J2EE首先要了解它的规范,下面我们一起看看它的十三个规范. 1,JDBC(Java Data ...
- 3、python的传入参数
转载:https://blog.csdn.net/abc_12366/article/details/79627263 1.位置参数: def func(a, b): print(a+b) func( ...
- jqgrid控件列分组
<%-- builed by manage.aspx.cmt [ver:2014.48.11] at 2014/10/11 16:48:33 --%> <%@ Page Langua ...
- CodeBlocks调试功能(转)
转自:迂者-贺利坚 http://blog.csdn.net/sxhelijian/article/details/15026159 示例代码: #include <iostream> u ...
- html5 存储方式
localstorage(永久保存)&&sessionstorage(重新打开浏览器会消失) sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在 ...
- Android Tablayout属性介绍
1.添加依赖 compile 'com.android.support:design:26.0.0-alpha1' 2.属性 改变选中字体的颜色app:tabSelectedTextColor=&qu ...
- 深入理解python中可迭代对象,迭代器,生成器
英文原文出处:Iterables vs. Iterators vs. Generators 在python学习中,通常会陷入对以下几个相关概念之间的确切差异的困惑中: a container(容器) ...