BZOJ 4011 HNOI2015 落忆枫音 DAG上的dp(实际上重点在于分析)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4011

题意概述:给出一张N点的DAG(连通),点1的入度为0。现在加一条原图没有的边,问有多少种方案可使这张图变成一棵以1为根的有向树(即每个点的父亲指向自己)。

N<=100000,M<=min(200000,N(N-1)/2).

实际上这个题主要在分析(感觉终于开始自己做出省选题了)。

先看没有加边的情况,yy一下你发现这种情况的答案就是所有rd(入度)不为0的点rd相乘。道理是只要给每个点指定一个父亲,由于原图是DAG,相当于逆着边走,由于题目有保证1可以到达每个点,所以每个点一定可以反着走到1。

加边的情况?注意到加边之后可能还是DAG,一样的处理。如果不是DAG说明有环。答案分成两部分,不用新加的边的方案+用新加的边的合法方案。新加的边的合法方案又等于新加边所有方案-不合法方案(所有方案指的是父亲乱指,不合法方案指的是指出了环)。

重点在于计算不合法方案数。来分析一下环的性质,可以发现新加的边一定在环上,且我们已经计算的方案中任意一个点的rd为1,这种情况下如果图不连通可以有很多个环,但是所有环一定经过新加的边所以只有一个环。于是暴力地我们可以枚举所有的环,把环上所有点的rd变成1,其它所有点的rd相乘,得到的方案数就是这个环对当前答案的不合法贡献,减掉。

这个算法随便一卡就成了O(N^2)的优秀算法了,怎么优化呢?令加的边为x->y,可以发现任意一个环一定是从y出发经过一些点走到x经过x->y这条边回到y,也就是说环的数量就是原图中y到x的路径数量。注意到所有点的rd之积mul是不变的,在暴力算法中每条环上的点rd变成1,也就对应y到x的路径上的每一个点rd变成1,如果y到x的一条路径上的rd乘积为_mul,那么这条路径对不合法答案的贡献就是mul/_mul,最后是所有的不合法贡献相加之后从答案中扣除,所以可以设计出这样的dp方程:

令f(i)表示i到x的路径上的点对答案的不合法贡献数,f(i)=sum{ f(j) | j->i } / rd[i]。(实际上这个dp就是对暴力的一个优化而已)

特殊判断一些情况即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<queue>
 8 #include<set>
 9 #include<map>
10 #include<vector>
11 #include<cctype>
12 using namespace std;
13 const int maxn=100005;
14 const int maxm=200005;
15 const int mo=1000000007;
16
17 int N,M,X,Y;
18 struct edge{ int to,next; }E[maxm];
19 int first[maxn],np,rd[maxn],sccno[maxn],sccsz[maxn],scc_cnt,dfs_clock,dfn[maxn],low[maxn];
20 int stk[maxn],top,inv[maxn],f[maxn],ID,mul;
21
22 void add_edge(int u,int v)
23 {
24     E[++np]=(edge){v,first[u]};
25     first[u]=np;
26 }
27 void data_in()
28 {
29     scanf("%d%d%d%d",&N,&M,&X,&Y);
30     int x,y;
31     for(int i=1;i<=M;i++){
32         scanf("%d%d",&x,&y);
33         rd[y]++;
34         add_edge(x,y);
35     }
36     add_edge(X,Y);
37     inv[1]=1;
38     for(int i=2;i<=N;i++)
39         inv[i]=1ll*inv[mo%i]*(mo-mo/i)%mo;
40 }
41 void tarjan_scc(int i)
42 {
43     low[i]=dfn[i]=++dfs_clock;
44     stk[++top]=i;
45     for(int p=first[i];p;p=E[p].next){
46         int j=E[p].to;
47         if(dfn[j]){
48             if(!sccno[j]) low[i]=min(low[i],dfn[j]);
49             continue;
50         }
51         tarjan_scc(j);
52         low[i]=min(low[i],low[j]);
53     }
54     if(low[i]==dfn[i]){
55         scc_cnt++;
56         while(1){
57             sccno[stk[top]]=scc_cnt;
58             sccsz[scc_cnt]++;
59             if(stk[top--]==i) break;
60         }
61     }
62 }
63 int dp(int i)
64 {
65     if(f[i]) return f[i];
66     if(i==X) return f[i]=mul;
67     for(int p=first[i];p;p=E[p].next){
68         int j=E[p].to;
69         if(sccno[j]!=ID||i==X&&j==Y) continue;
70         f[i]=(f[i]+dp(j))%mo;
71     }
72     return f[i]=1ll*f[i]*inv[rd[i]]%mo;
73 }
74 void work()
75 {
76     int ans=1;
77     for(int i=2;i<=N;i++) ans=1ll*ans*rd[i]%mo;
78     if(Y!=1){
79         ans=1ll*ans*inv[rd[Y]]%mo*(rd[Y]+1)%mo;
80         tarjan_scc(1);
81         int MAX=0;
82         for(int i=1;i<=N;i++)
83             if(sccsz[sccno[i]]>MAX) MAX=sccsz[sccno[i]],ID=sccno[i];
84         if(MAX>1){
85             rd[X]=rd[Y]=mul=1;
86             for(int i=2;i<=N;i++) mul=1ll*mul*rd[i]%mo;
87             ans=(ans-dp(Y)+mo)%mo;
88         }
89     }
90     printf("%d\n",ans);
91 }
92 int main()
93 {
94     data_in();
95     work();
96     return 0;
97 }

原文地址:https://www.cnblogs.com/KKKorange/p/8485464.html

时间: 2024-10-27 00:49:22

BZOJ 4011 HNOI2015 落忆枫音 DAG上的dp(实际上重点在于分析)的相关文章

BZOJ 4011: [HNOI2015]落忆枫音( dp )

DAG上有个环, 先按DAG计数(所有节点入度的乘积), 然后再减去按拓扑序dp求出的不合法方案数(形成环的方案数). -------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long l

BZOJ 4011 HNOI2015 落忆枫音 拓扑序DP

题目大意:给定一张有向无环图,现在要求加入一条边,求加入后以1为根的树形图个数 首先不考虑加入的这条边,那么这个图是一个DAG 由朱刘算法的推论可知,如果除根节点外每个点都选择一条入边,由于没有环,因此一定会形成一个树形图 因此答案就是∏ni=2degreei 其中degreei表示第i个点的入度 现在加入这条边之后,我们仍然可以套用这个公式,但是这样就会有一些不合法的方案被统计进来,我们需要把这些不合法的方案减掉 一个方案如果不合法,那么一定会形成一个环,而环一定包含新加入的那条边 因此我们减

【BZOJ 4011】 [HNOI2015]落忆枫音

4011: [HNOI2015]落忆枫音 Time Limit: 10 Sec Memory Limit: 512 MB Submit: 244 Solved: 137 [Submit][Status][Discuss] Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出 这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂--我们也不可能再见 到你姐姐吧.」 恒逸给出了一个略微无厘头的回答.枫茜听后笑了笑.

BZOJ4011 [HNOI2015]落忆枫音

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出 这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂--我们也不可能再见 到你姐姐吧.」 恒逸给出了一个略微无厘头的回

[HNOI2015] 落忆枫音

题目描述 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂......我们也不可能再见到你姐姐吧.」 恒逸给出了一个略微无厘头的回答.枫茜听后笑了笑. 「那你仔细观察过枫叶吗?」 说罢,枫茜伸手,接住了一片飘落的枫叶. 「其实每一片枫叶都是有灵魂的.你看,枫叶上不是有这么多脉络吗?我听说,枫叶上有一些特殊的位置,就和人的穴位一样.脉络都是连接在这些穴位之间的.枫树的灵魂流过每片枫叶

luogu3244 bzoj4011 HNOI2015 落忆枫音

这道题目题面真长,废话一堆. 另外:这大概是我第一道独立做出来的HNOI2011年以后的题目了吧.像我水平这么差的都能做出来,dalao您不妨试一下自己想想? 题目大意:给一个DAG,其中1号点没有入度,现在新加入一条不重复的边,使得它可能有环.求它的生成子图个数,使得子图正好包含N-1条边且1号点与其它的所有点连通. 题目分析: 我们首先要发现这是一个树的结构!有向的树. 分析树的特点,树的父亲只有一个,我们不妨从这里入手. 在这一个生成子图中,谁是谁的父亲? 我们知道1号点一定是root,这

【BZOJ】【4011】【HNOI2015】落忆枫音

拓扑排序+DP 题解:http://blog.csdn.net/PoPoQQQ/article/details/45194103 http://www.cnblogs.com/mmlz/p/4448742.html 通过转化……路径外的$degree_i$的乘积转化成所有点的degree之积除以路径内的,所以用到逆元…… PoPoQQQ的线性筛逆元好神奇啊……>_< OrzOrz 1 /******************************************************

【bzoj4011 hnoi2015】落忆枫音

题目描述 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂......我们也不可能再见到你姐姐吧.」 恒逸给出了一个略微无厘头的回答.枫茜听后笑了笑. 「那你仔细观察过枫叶吗?」 说罢,枫茜伸手,接住了一片飘落的枫叶. 「其实每一片枫叶都是有灵魂的.你看,枫叶上不是有这么多脉络吗?我听说,枫叶上有一些特殊的位置,就和人的穴位一样.脉络都是连接在这些穴位之间的.枫树的灵魂流过每片枫叶

[HNOI 2015]落忆枫音

Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出 这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂……我们也不可能再见 到你姐姐吧.」 恒逸给出了一个略微无厘头的回答.枫茜听后笑了笑. 「那你仔细观察过枫叶吗?」 说罢,枫茜伸手,接住了一片飘落的枫叶. 「其实每一片枫叶都是有灵魂的.你看,枫叶上不是有这么多脉络吗?我听说, 枫叶上有一些特殊的位置,就和人的穴位一样.脉络都是连接在这些穴位之间的. 枫树的灵