bzoj4011 [HNOI2015]落忆枫音
Description
「恒逸,你相信灵魂的存在吗?」
郭恒逸和姚枫茜漫步在枫音乡的街道上。望着漫天飞舞的红枫,枫茜突然问出这样一个问题。
「相信吧。不然我们是什么,一团肉吗?要不是有灵魂……我们也不可能再见到你姐姐吧。」
恒逸给出了一个略微无厘头的回答。枫茜听后笑了笑。
「那你仔细观察过枫叶吗?」
说罢,枫茜伸手,接住了一片飘落的枫叶。
「其实每一片枫叶都是有灵魂的。你看,枫叶上不是有这么多脉络吗?我听说,枫叶上有一些特殊的位置,就和人的穴位一样。脉络都是连接在这些穴位之间的。枫树的灵魂流过每片枫叶的根部,沿着这些脉络,慢慢漫进穴位,沁入整片枫叶。也是因为这个原因,脉络才都是单向的,灵魂可不能倒着溜回来呢。」
恒逸似懂非懂地点了点头。枫茜接着说了下去。
「正是因为有了灵魂,每片枫叶才会与众不同。也正是因为有了灵魂,每片枫叶也都神似其源本的枫树,就连脉络也形成了一棵树的样子。但如果仔细看的话,会发现,在脉络树之外,还存在其它的非常细的脉络。虽然这些脉络并不在树上,但他们的方向也同样顺着灵魂流淌的方向,绝不会出现可能使灵魂倒流的回路。」
恒逸好像突然想到了什么。
「那这些脉络岂不是可以取代已有的脉络,出现在脉络树上?」
枫茜闭上了眼睛。
「是啊,就是这样。脉络树并不是唯一的。只要有一些微小的偏差,脉络树就可能差之万里,哪怕是在这同一片枫叶上。就像我们的故事,结局也不是唯一的。只要改变一个小小的选项,故事流程可能就会被彻底扭转。」
「真是深奥啊……」
恒逸盯着这片红枫,若有所思地说。枫茜继续说道。
「还不止如此呢。所有的脉络都不会永恒存在,也不会永恒消失。不管是脉络树上的脉络,还是之外的细小脉络,都是如此。存在的脉络可能断开消失,消失的脉络也可能再次连接。万物皆处在永恒的变化之中,人与人之间的羁绊也是。或许有一天,我们与大家的羁绊也会如同脉络一样,被无情地斩断。或许我们也终将成为“枫音乡的过客”。或许这一切都会是必然,是枫树的灵魂所决定的……」
枫茜的眼角泛起了几滴晶莹剔透的泪珠。恒逸看着这样的枫茜,将她抱入怀中。
「别这样想,枫茜。就算脉络断开,也有可能还会有新的脉络树,也还会与枫树的根相连。这样的话,我们的羁绊仍然存在,只是稍微绕了一些远路而已。无论如何,我都不会离开你的。因为你是我穷尽一生所寻找的,我的真恋啊!」 两人的目光对上了。枫茜幸福地笑了,把头埋进了恒逸的怀抱。从远方山上的枫林中,传来了枫的声音。
【问题描述】
不妨假设枫叶上有 n个穴位,穴位的编号为 1 ~ 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 为根的脉络树方案数。
由于方案可能有太多太多,请输出方案数对 1,000,000,007 取模得到的结果。
Input
输入文件的第一行包含四个整数 n、m、x和y,依次代表枫叶上的穴位数、脉络数,以及要添加的脉络是从穴位 x连向穴位y的。
接下来 m行,每行两个整数,由空格隔开,代表一条脉络。第 i 行的两个整数为ui和vi,代表第 i 条脉络是从穴位 ui连向穴位vi的。
Output
输出一行,为添加了从穴位 x连向穴位 y的脉络后,枫叶上以穴位 1 为根的脉络树的方案数对 1,000,000,007取模得到的结果。
Sample Input
4 4 4 3
1 2
1 3
2 4
3 2
1 2
1 3
2 4
3 2
Sample Output
3
HINT
对于所有测试数据,1 <= n <= 100000,n - 1 <= m <= min(200000, n(n – 1) / 2), 1 <= x, y, ui, vi <= n。
正解:拓扑序dp。
一开始的图是一个DAG,而DAG的树形图方案数是除了1以外的所有点入度连乘。
当我们加入一条边以后,我们考虑如何计算答案。我们首先可以仍然算出所有点入度连乘的积,如果y=1或x=y直接输出原DAG的方案数就行。我们算出积以后,考虑如何去掉不合法的情况。所有不合法的情况肯定是y到x有一条路径,且x连y的情况。
我们要减去的就是∑S∏d[i],i∉S,其中S为y到x的一条路径的点集,这样当其它点形成一棵树,再与这条路径相连,则必定是一个环。
然后我们可以考虑dp。我们设f[i]表示y->i的点集方案数,那么我们可以得到如下转移:f[i]=∑f[j]/d[i],其中j->i。初始状态为f[y]=∏d[i]/d[y]。
然后这道题就能顺利解决了。。话说这个题面是什么鬼。。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define inf (1<<30) 15 #define N (100010) 16 #define il inline 17 #define RG register 18 #define ll long long 19 #define rhl 1000000007 20 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 21 22 using namespace std; 23 24 struct edge{ ll nt,to; }g[2*N]; 25 26 ll head[N],f[N],d[N],dd[N],inv[N],q[N],n,m,x,y,num,ans; 27 28 il ll gi(){ 29 RG ll x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 30 if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; 31 } 32 33 il void insert(RG ll from,RG ll to){ g[++num]=(edge){head[from],to},head[from]=num; return; } 34 35 il ll qpow(RG ll a,RG ll b){ RG ll ans=1; while (b){ if (b&1) (ans*=a)%=rhl; (a*=a)%=rhl,b>>=1; } return ans; } 36 37 il void topsort(RG ll st,RG ll ed){ 38 RG ll h=0,t=0; 39 for (RG ll i=1;i<=n;++i) if (!dd[i]) q[++t]=i; 40 f[st]=ans*inv[st]%rhl; 41 while (h<t){ 42 RG ll x=q[++h],v; 43 for (RG ll i=head[x];i;i=g[i].nt){ 44 v=g[i].to,dd[v]--; 45 (f[v]+=f[x]*inv[v])%=rhl; 46 if (!dd[v]) q[++t]=v; 47 } 48 } 49 ans+=rhl-f[ed]; ans%=rhl; 50 } 51 52 il void work(){ 53 n=gi(),m=gi(),x=gi(),y=gi(); RG ll u,v; ans=1; 54 for (RG ll i=1;i<=m;++i) u=gi(),v=gi(),d[v]++,insert(u,v); 55 if (x!=y) d[y]++; for (RG ll i=2;i<=n;++i) (ans*=d[i])%=rhl; 56 if (y==1 || x==y){ printf("%lld\n",ans); return; } 57 for (RG ll i=1;i<=n;++i) inv[i]=qpow(d[i],rhl-2),dd[i]=d[i]; dd[y]--,d[y]--; 58 topsort(y,x); printf("%lld\n",ans); return; 59 } 60 61 int main(){ 62 File("maple"); 63 work(); 64 return 0; 65 }