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=1k​du[ai]=∏du[w]\frac{∏du[i]}{∏_{i=1}^{k}​du[a_i]}=∏du[w]∏i=1k​​du[ai​]∏du[i]​=∏du[w],其中du[i]du[i]du[i]表示在原图上的入度.

因为x=akx=a_kx=ak​,为了表示方便,不妨令f[x]=∏du[i]∏i=1k​du[ai]f[x]=\frac{∏du[i]}{∏_{i=1}^{k}​du[a_i]}f[x]=∏i=1k​​du[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&MediumSpace;p);a^{p-1}=1(mod \:p);ap−1=1(modp);

也就是

ap−2⋅a=1(mod&MediumSpace;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上的动态规划问题,朱刘定理,乘法逆元)相关推荐

  1. luogu P3244 [HNOI2015]落忆枫音

    传送门 md这题和矩阵树定理没半毛钱关系qwq 首先先不考虑有环,一个\(DAG\)个外向树个数为\(\prod_{i=2}^{n}idg_i(\)就是\(indegree_i)\),因为外向树每个点 ...

  2. 洛谷 P3244 / loj 2115 [HNOI2015] 落忆枫音 题解【拓扑排序】【组合】【逆元】

    组合计数的一道好题.什么非主流题目 题目背景 (背景冗长请到题目页面查看) 题目描述 不妨假设枫叶上有 \(n​\) 个穴位,穴位的编号为 \(1\sim n​\).有若干条有向的脉络连接着这些穴位. ...

  3. [HNOI2015] 落忆枫音

    题目描述 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂......我们也不可能再 ...

  4. Bzoj4011 [HNOI2015]落忆枫音

    Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 983  Solved: 533 Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫 ...

  5. BZOJ4011:[HNOI2015]落忆枫音(DP,拓扑排序)

    Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出这样一个问题.  「相信吧.不然我们是什么,一团肉吗?要不是有灵魂--我们也 ...

  6. bzoj4011[HNOI2015]落忆枫音

    http://www.lydsy.com/JudgeOnline/problem.php?id=4011 记新加入的边的起点为$x$,终点为$y$ 首先,我们先考虑新加入的边没有构成环的情况,即在原图 ...

  7. [HNOI2015]落忆枫音

    题目描述 不妨假设枫叶上有 n个穴位,穴位的编号为 1 ~  n.有若干条有向的脉络连接 着这些穴位.穴位和脉络组成一个有向无环图--称之为脉络图(例如图 1),穴 位的编号使得穴位 1 没有从其他穴 ...

  8. luogu3244 bzoj4011 HNOI2015 落忆枫音

    这道题目题面真长,废话一堆. 另外:这大概是我第一道独立做出来的HNOI2011年以后的题目了吧.像我水平这么差的都能做出来,dalao您不妨试一下自己想想? 题目大意:给一个DAG,其中1号点没有入 ...

  9. 【题解】 [HNOI2015]落忆枫音 (拓扑排序+dp+容斥原理)

    原题戳我 Solution: (部分复制Navi_Aswon博客) 解释博客中的两个小地方: \[\sum_{\left(S是G中y→x的一条路径的点集\right))}\prod_{2≤j≤n,(j ...

最新文章

  1. Linux常用命令及技巧1
  2. 求长度的另一种方法(+obj).Length
  3. C++ BigInt模板手打
  4. php手册数组函数,PHP - Manual手册 - 函数参考 - Array 数组函数 - array_diff计算数组的差集...
  5. 如何避免眼高手低?(转载)----希望初入职场的朋友共勉吧!
  6. Linux内存中加载二进制,linux – 程序退出后二进制文件会留在内存中吗?
  7. SQL Server Error 15404解决方案
  8. javaweb 学习资源
  9. 内存经销商穷困潦倒 七元午饭都赊账
  10. IJCAI 2021 | 腾讯和复旦联合出品:Adv-Makeup人脸黑盒攻击对抗算法
  11. vscode 支持ansi_vscode terminal美化
  12. 淘宝店的图片哪里来的
  13. iPhone UI 元素大小
  14. rsync+inotify实现数据实时同步备份
  15. 网线制作ppt_制作网线图解讲解.ppt
  16. 二阶无源低通滤波器幅频特性曲线_二阶无源滤波器
  17. iphone模拟器安装app
  18. c语言中的EOF是什么意思
  19. 计算机显示器的分辨率可以调节吗,电脑分辨率何如调?电脑无法调整屏幕分辨率怎么办?...
  20. eclipse java验证码_spring整合kaptcha验证码

热门文章

  1. android配置wifi,Android WIFI检测与设置
  2. PHP获取系统时间的方法(毫秒数)
  3. 我的第一份实习——钛动科技
  4. 计算机配置64位,任何电脑都可以装64位系统吗|是不是所有的电脑都可以装64位系统...
  5. 牧场上的草泥马(游荡的奶牛)
  6. 工作学习正确坐姿坐姿提升注意力
  7. APP混合应用之web页面处理
  8. 相比微信、支付宝支付,apple pay支付优缺点是什么呢?
  9. 程序员必读经典书籍 (转)
  10. 网络表示学习Network Representation Learning/Embedding