P3244 [HNOI2015]落忆枫音(DAG上的动态规划问题,朱刘定理,乘法逆元)
P3244 [HNOI2015]落忆枫音
样例太坑了!竟然和题目描述给的图不一样!
题目描述
给定一张有向无环图,这张图满足一个性质:以点1为根节点,保证至少有一棵有向树,连接所有的节点.
现在向这张图中新增一条给定的有向边,求增加这条有向边之后,有向无环图中的有向树的数量.
答案可能会很大,所以要对1e9+71e9+71e9+7取模.
题目解析
首先,我们有这样一个定理:
朱刘定理:一张有向无环图上,生成树的数量为∏du[i]∏du[i]∏du[i],其中du[i]du[i]du[i]表示一个点的入度.
证明:显然在构建生成树时,每个点被选择的方法数就是这个点的入度,由乘法原理可知,不同生成树的数量就是∏du[i]∏du[i]∏du[i].
但是,加入一条有向边后的新图,可能会出现环. 由于树上是没有环的,所以如果我们直接用∏du[i]∏du[i]∏du[i]作为生成树的数量,可能会多算环的情况,导致答案错误.
如图,左边为原图,右边为增加了一条边的图. 可以发现,按照朱刘定理算出来的值为4,多计算了2−>4−>3−>22->4->3->22−>4−>3−>2的情况.
怎么办?当然是消除影响了!
假设添加的一条边是s−>ts->ts−>t,那么不在原图t−>st->st−>s路径上的点的入度的乘积之和就是出现环的方案数.
我们假设当前从原图t−>st->st−>s的路径经过了节点a1,a2...aka_1,a_2...a_ka1,a2...ak,这条路径总共有kkk条边.
显然剩下的n−kn-kn−k个节点就有n−k−1n-k-1n−k−1条边.
而根据我们的朱刘算法,剩下的n−kn-kn−k个节点组合成的生成树的数量就是∏du[w]∏du[w]∏du[w],www表示不在当前原图t−>st->st−>s的路径上的点.
同样根据朱刘算法的原理,它保证每个点的入度都为1(因为每个节点都是从若干入度中选择一个入度作为父亲,根节点除外),又因为当前我们研究的环的形态和其上的点都是固定的,不合法的方案数就只和不在环上的点组合成的树的数目相关,所以在当前环下不合法的情况数,就是剩下的n−kn-kn−k个节点组合成的生成树的数量∏du[w]∏du[w]∏du[w].
而环可能有许多个,所以我们要把他们的不合法的方案数相加.
注意:路径有许多条,我们是算出每一条路径的、不在当前路径上的点能构建的生成树数,再相加.
也就是说,可能一个点不在现在这个环上,但是会在另外一个环上. 所以在这个环的情况它对不合法方案数有贡献,但是在另外一个环的时候它对不合法方案无贡献.
那我们考虑这个不合法方案数怎么算.
显然地,现在的这种表示方式很不方便计算. 我们得求出每一条从t−>st->st−>s的路径,然后再求不在其上的点,最后相乘.
那么就试着把信息整合一下.
不难发现,实际上∏du[i]∏i=1kdu[ai]=∏du[w]\frac{∏du[i]}{∏_{i=1}^{k}du[a_i]}=∏du[w]∏i=1kdu[ai]∏du[i]=∏du[w],其中du[i]du[i]du[i]表示在原图上的入度.
因为x=akx=a_kx=ak,为了表示方便,不妨令f[x]=∏du[i]∏i=1kdu[ai]f[x]=\frac{∏du[i]}{∏_{i=1}^{k}du[a_i]}f[x]=∏i=1kdu[ai]∏du[i].
而这个东西是支持状态的转移的. 比如说,假设从uuu到vvv有一条边,那么f[v]+=f[u]du[v]f[v]+=\frac{f[u]}{du[v]}f[v]+=du[v]f[u].
我们使用记忆化搜索即可.
我们会发现,从ttt开始搜索向sss的路径不容易,因为这需要遍历整张图.
所以我们反向建图,在回溯的时候更新状态.
void dfs(int u){if(u==yy)return ; if(vis[u])return ;if(f[u]>=0)return ;//f初始状态为-1vis[u]=true;f[u]=0;for(int i=head[u];i;i=e[i].next ){int v=e[i].v ;dfs(v);f[u]+=f[v]/in[u];//不同的从t->s路径的不合法方案数要求和f[u]%=mod;}vis[u]=false;return ;
}
//这部分写在主函数中for(int i=1,u,v;i<=m;i++){scanf("%d%d",&u,&v);add(v,u);}ans=1;cnt=1;in[1]=1;for(int i=1;i<=n;i++){ans=(ans*in[i])%mod;//ans表示原图的方案数if(i==yy)cnt=cnt*(in[i]+1)%mod;//cnt表示新图直接表示的方案数else cnt=(cnt*in[i])%mod;}f[yy]=ans/in[yy];//防止重名,x->xx,y->yyf[yy]%=mod;dfs(xx);
更新完之后的f[x]f[x]f[x]就是总的不合法路径数,答案就是cnt−f[x]cnt-f[x]cnt−f[x]了.
tips:tips:tips:
1.不用记忆化会T
2.区分好什么时候用ans,什么时候用cnt
然后你会惊喜地发现你拿到了30pts30pts30pts的好成绩!
由于这里出现了膜意义下的除法运算,我们不得不求逆元以保证除法的正确性.
观察发现1e9+71e9+71e9+7是一个很大的质数,因此我们采用费马小定理求逆元.
ap−1=1(mod p);a^{p-1}=1(mod \:p);ap−1=1(modp);
也就是
ap−2⋅a=1(mod p).a^{p-2}\cdot a=1(mod \:p).ap−2⋅a=1(modp).
用快速幂可求.
其他细节:
1.1.1.连接的这条边是连向点1的,那么这条边就不会对答案造成影响.
2.2.2.另外,取模的时候,为了保证最后的结果为正数,要这样写:
cnt=(cnt-f[xx]+mod)%mod;
程序实现
#include<bits/stdc++.h>
#define maxn 100010
#define mod 1000000007
#define ll long long
using namespace std;
struct edge{int v,next;
}e[maxn<<1];
int tot,head[maxn];
ll in[maxn];
void add(int u,int v){e[++tot].v =v;e[tot].next =head[u];head[u]=tot;in[u]+=1;
}
int n,m,xx,yy;
ll ans,cnt;
bool vis[maxn];
ll f[maxn];
ll fast_pow(ll a,ll b){ll c=1;while(b){if(b&1)c=(a*c)%mod;a=(a*a)%mod;b>>=1;}return c;
} //费马小定理(快速幂)求逆元
void dfs(int u){if(u==yy)return ; if(vis[u])return ;//记忆化搜索的形式vis[u]=true;for(int i=head[u];i;i=e[i].next ){int v=e[i].v ;dfs(v);f[u]+=(f[v]*fast_pow(in[u],mod-2)%mod)%mod;f[u]%=mod;}return ;
}
int main(){scanf("%d%d%d%d",&n,&m,&xx,&yy);for(int i=1,u,v;i<=m;i++){scanf("%d%d",&u,&v);add(v,u);//反向建边}ans=1;cnt=1;in[1]=1;for(int i=1;i<=n;i++){ans=(ans*in[i])%mod;if(i==yy)cnt=(cnt*(in[i]+1))%mod;else cnt=(cnt*in[i])%mod;}f[yy]=(ans*fast_pow(in[yy],mod-2))%mod;//求终点的不合法路径数f[yy]%=mod;dfs(xx);cnt=(cnt-f[xx]+mod)%mod;//保证结果为正if(yy==1||xx==yy)printf("%lld\n",ans);//特判else printf("%lld\n",cnt);return 0;
}
P3244 [HNOI2015]落忆枫音(DAG上的动态规划问题,朱刘定理,乘法逆元)相关推荐
- luogu P3244 [HNOI2015]落忆枫音
传送门 md这题和矩阵树定理没半毛钱关系qwq 首先先不考虑有环,一个\(DAG\)个外向树个数为\(\prod_{i=2}^{n}idg_i(\)就是\(indegree_i)\),因为外向树每个点 ...
- 洛谷 P3244 / loj 2115 [HNOI2015] 落忆枫音 题解【拓扑排序】【组合】【逆元】
组合计数的一道好题.什么非主流题目 题目背景 (背景冗长请到题目页面查看) 题目描述 不妨假设枫叶上有 \(n\) 个穴位,穴位的编号为 \(1\sim n\).有若干条有向的脉络连接着这些穴位. ...
- [HNOI2015] 落忆枫音
题目描述 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂......我们也不可能再 ...
- Bzoj4011 [HNOI2015]落忆枫音
Time Limit: 10 Sec Memory Limit: 512 MB Submit: 983 Solved: 533 Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫 ...
- BZOJ4011:[HNOI2015]落忆枫音(DP,拓扑排序)
Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂--我们也 ...
- bzoj4011[HNOI2015]落忆枫音
http://www.lydsy.com/JudgeOnline/problem.php?id=4011 记新加入的边的起点为$x$,终点为$y$ 首先,我们先考虑新加入的边没有构成环的情况,即在原图 ...
- [HNOI2015]落忆枫音
题目描述 不妨假设枫叶上有 n个穴位,穴位的编号为 1 ~ n.有若干条有向的脉络连接 着这些穴位.穴位和脉络组成一个有向无环图--称之为脉络图(例如图 1),穴 位的编号使得穴位 1 没有从其他穴 ...
- luogu3244 bzoj4011 HNOI2015 落忆枫音
这道题目题面真长,废话一堆. 另外:这大概是我第一道独立做出来的HNOI2011年以后的题目了吧.像我水平这么差的都能做出来,dalao您不妨试一下自己想想? 题目大意:给一个DAG,其中1号点没有入 ...
- 【题解】 [HNOI2015]落忆枫音 (拓扑排序+dp+容斥原理)
原题戳我 Solution: (部分复制Navi_Aswon博客) 解释博客中的两个小地方: \[\sum_{\left(S是G中y→x的一条路径的点集\right))}\prod_{2≤j≤n,(j ...
最新文章
- Linux常用命令及技巧1
- 求长度的另一种方法(+obj).Length
- C++ BigInt模板手打
- php手册数组函数,PHP - Manual手册 - 函数参考 - Array 数组函数 - array_diff计算数组的差集...
- 如何避免眼高手低?(转载)----希望初入职场的朋友共勉吧!
- Linux内存中加载二进制,linux – 程序退出后二进制文件会留在内存中吗?
- SQL Server Error 15404解决方案
- javaweb 学习资源
- 内存经销商穷困潦倒 七元午饭都赊账
- IJCAI 2021 | 腾讯和复旦联合出品:Adv-Makeup人脸黑盒攻击对抗算法
- vscode 支持ansi_vscode terminal美化
- 淘宝店的图片哪里来的
- iPhone UI 元素大小
- rsync+inotify实现数据实时同步备份
- 网线制作ppt_制作网线图解讲解.ppt
- 二阶无源低通滤波器幅频特性曲线_二阶无源滤波器
- iphone模拟器安装app
- c语言中的EOF是什么意思
- 计算机显示器的分辨率可以调节吗,电脑分辨率何如调?电脑无法调整屏幕分辨率怎么办?...
- eclipse java验证码_spring整合kaptcha验证码
热门文章
- android配置wifi,Android WIFI检测与设置
- PHP获取系统时间的方法(毫秒数)
- 我的第一份实习——钛动科技
- 计算机配置64位,任何电脑都可以装64位系统吗|是不是所有的电脑都可以装64位系统...
- 牧场上的草泥马(游荡的奶牛)
- 工作学习正确坐姿坐姿提升注意力
- APP混合应用之web页面处理
- 相比微信、支付宝支付,apple pay支付优缺点是什么呢?
- 程序员必读经典书籍 (转)
- 网络表示学习Network Representation Learning/Embedding