zoukankan      html  css  js  c++  java
  • BZOJ 4011 HNOI2015 落忆枫音 DAG上的dp(实际上重点在于分析)

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

    题意概述:给出一张N点的DAG(从1可以到达所有的点),点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(?反正是个指数级别) 的优秀算法了,怎么优化呢?令加的边为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
    ",ans);
    91 }
    92 int main()
    93 {
    94     data_in();
    95     work();
    96     return 0;
    97 }
  • 相关阅读:
    刘翔那点事
    网站建站模板
    搞笑!from 饮水思源
    我de虚拟经济学系列第一章 经济危机拼命建桥
    IT民工系列——c#操作Microsoft IE,实现自动登录吧!
    商业智能的发展及其应用
    我de虚拟经济学系列第三章 常见的致富之路
    IT民工系列——c#操作EditGrid,自己做一个在线Excel数据库吧!
    Asp.net下的Singleton模式
    asp.net 控件功能小结
  • 原文地址:https://www.cnblogs.com/KKKorange/p/8485464.html
Copyright © 2011-2022 走看看