zoukankan      html  css  js  c++  java
  • bzoj 4011

    看了好多篇题解才看懂的题,我实在太菜了...

    首先根据一个我不知道的算法,可以证明在没有加入新的边的时候,原图的所有生成树的方案数就是所有点(除1以外)的度之积

    那么在新加入这条边之后,我们仍然可以这样计算,但是会产生一种问题:就是会出现环!

    所以我们需要利用一些容斥,把不合法的情况去掉

    接下来我们考虑如何算出不合法的情况

    由于原图是一个有向无环图,所以在原图中怎么选都不会出现环,所以多的一条边一定在环内!

    那么如果出现了环,一定是从多的边的终点到起点的一条路径

    所以我们只需要找出终点到起点的路径数量即可

    那么就进行一个拓扑排序求一下即可

    考虑一下怎么求:

    首先记状态:dp[i]表示从终点到i的路径数量

    然后考虑初值:dp[ed]=初始方案/终点入度

    (关于这里为什么要除掉一个入度:因为原来的初始方案中是包含这一点的入度的,所以要除掉这个入度)

    最后ans去掉这一部分即可

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #define mode 1000000007
    #define ll long long
    using namespace std;
    struct Edge
    {
    	int next;
    	int to;
    }edge[200005];
    int n,m,st,ed;
    ll inr[100005];
    ll rinr[100005];
    int head[100005];
    ll inv[300005];
    int cnt=1;
    ll ans=1;
    ll dp[100005];
    void init()
    {
    	inv[0]=inv[1]=1;
    	for(int i=2;i<=300000;i++)
    	{
    		inv[i]=(mode-mode/i)*inv[mode%i]%mode;
    	}
    	memset(head,-1,sizeof(head));
    	cnt=1;
    }
    void add(int l,int r)
    {
    	edge[cnt].next=head[l];
    	edge[cnt].to=r;
    	head[l]=cnt++;
    }
    ll bfs()
    {
    	dp[ed]=ans;
    	queue <int> M;
    	for(int i=1;i<=n;i++)
    	{
    		if(!inr[i])
    		{
    			M.push(i);
    		}
    	}
    	while(!M.empty())
    	{
    		int u=M.front();
    		M.pop();
    		dp[u]*=inv[rinr[u]];
    		dp[u]%=mode;
    		for(int i=head[u];i!=-1;i=edge[i].next)
    		{
    			int to=edge[i].to;
    			dp[to]+=dp[u];
    			dp[to]%=mode;
    			inr[to]--;
    			if(!inr[to])
    			{
    				M.push(to);
    			}
    		}
    	}
    	return ((ans-dp[st])%mode+mode)%mode;
    }
    int main()
    {
    	scanf("%d%d%d%d",&n,&m,&st,&ed);
    	init();
    	rinr[ed]=1;
    	for(int i=1;i<=m;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		inr[y]++;
    		rinr[y]++;
    	}
    	for(int i=2;i<=n;i++)
    	{
    		ans*=rinr[i];
    		ans%=mode;	
    	}
    	if(ed==1)
    	{
    		printf("%lld
    ",ans);
    	}else
    	{
    		printf("%lld
    ",bfs());
    	}
    	return 0;
    }
  • 相关阅读:
    codeforces570D Tree Requests
    codeforces600E Lomsat gelral
    BZOJ2001 [Hnoi2010]City 城市建设
    BZOJ2565 最长双回文串
    BZOJ4031 [HEOI2015]小Z的房间
    BZOJ2467 [中山市选2010]生成树
    SPOJ104 HIGH
    爆零系列—补题A
    DP一直是自己的弱势 开始练滚动数组——HDOJ4502
    HDOJ4550 卡片游戏 随便销毁内存的代价就是wa//string类的一些用法
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10764141.html
Copyright © 2011-2022 走看看