BZOJ 4011: [HNOI2015]落忆枫音

来源:互联网 发布:福窝网的爱福窝软件 编辑:程序博客网 时间:2024/05/05 02:44

题意:给一个有向无环图,然后叫一条边,问以1为根的生成树数量。

题面好长啊,出题人真能编故事…先考虑不加那条边,则么ans=d[2]d[3]d[4]…d[n].(d为入度)。因为在一个DAG中,只要除根以外的点每个点选一条入边,就能获得一棵生成树。现在考虑加了这条边,如果再这么算,就有可能出现环的情况,所以我们减去环的情况就能得到答案。考虑在DAG上DP,f[i]=sigma(除去t->i的路径上的点的度数乘积),利用拓扑排序进行转移。f[u]=sigma(f[v])/d[u](v是u的入点集合)。
Tips:写逆元

#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>#include<vector>#include<queue>using namespace std;const int maxn=100000+10;const int md=1000000007;long long ny[maxn],f[maxn];int d[maxn],td[maxn],n,s,t,m;long long bow(long long x,int y){  long long z=x,p=1;  while(y)  {    if(y&1) p=(p*z)%md;    z=(z*z)%md;y>>=1;  }  return p;}vector<int> g[maxn];queue<int> Q;int main(){  //freopen("4011.in","r",stdin);  //freopen("4011.out","w",stdout);  scanf("%d%d%d%d",&n,&m,&s,&t);  for(int i=1;i<=n;i++)    ny[i]=bow(i,md-2);  for(int i=1;i<=m;i++)  {    int x,y;scanf("%d%d",&x,&y);    g[x].push_back(y);    d[y]++;td[y]++;  }  long long ans=1;  d[t]++;  for(int i=2;i<=n;i++)   {    ans=(ans*d[i])%md;  }  f[t]=ans;  for(int i=1;i<=n;i++) if(!td[i]) Q.push(i);  if(t==1)  {    printf("%lld\n",ans);    return 0;  }  while(!Q.empty())  {    int x=Q.front();Q.pop();    f[x]=(f[x]*ny[d[x]])%md;    //cout<<x<<' '<<f[x]<<endl;    for(int i=0;i<g[x].size();i++)    {      int v=g[x][i];      f[v]=(f[v]+f[x])%md;      td[v]--;if(!td[v]) Q.push(v);    }  }  ans=(ans-f[s]+md)%md;  printf("%lld\n",ans);  return 0;}
0 0
原创粉丝点击