【bzoj4011】落忆枫音

Portal --> bzoj4011

Solution

​  这题。。看了一眼之后深陷矩阵树定理然后我看了一眼数据范围==

  注意到是有向无环图,DAG有十分多优秀的性质所以,这题需要充分利用这个条件

  首先考虑没有加边的时候,也就是单纯求这个DAG的生成树个数怎么做

​  其实仔细想一下不难得出答案就是各个点(除了\(1\)号点)的入度的乘积

​   

  然后我们看加了一条边之后会发生什么

1、这条边会形成自环:显然答案不变

2、这条边加入后不会形成环:那直接更新一下入度然后重新再算一遍就好了

3、这条边加入后会形成一个环:

​  重头戏

​  正着想其实。。不是特别便于统计,这个时候!正难则反!

​  我们考虑从直接按照DAG的方法算出来的答案中减去那些不合法的方案,那么怎么样的方案才是不合法的呢,仔细思考一下,应该就是满足以下两个条件:

​  (1)选了\((x,y)\)这条边(就是新连的那条)

​  (2)形成了一个环

​  然后因为原来的图是DAG,这个环显然应该是包含\((x,y)\)这条边的,或者更加直观地说,这个环应该是\((x,y)\)这条边和一条从\(y\)\(x\)的路径组成的

​  那么现在问题就转化成了统计从\(y\)\(x\)的路径的“方案数”,这里的方案数要打引号是因为。。更准确地说应该是确保选边方案中存在一条\(y\)\(x\)路径并且包含\((x,y)\)这条边的生成树个数,具体的统计其实就跟普通的DAG路径计数一样的套路,拓扑排序一波

​  我们用\(val[x]\)表示\(y\)\(x\)路径的“方案数”,初始化就是\(val[y]=\)按照DAG方式算出来的答案,然后每一个点转移时候的贡献应该是\(val[i]/in[i]\),其中\(in[i]\)表示的是在加入\((x,y)\)这条边之后\(i\)点的入度,具体为什么的话是因为。。走到\(i\)的时候,\(i\)的前驱其实已经确定了,所以没有\(in[i]\)种选择

​   

​  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e5+10,MOD=1e9+7;
struct xxx{
	int y,nxt;
}a[N*2];
queue<int> q;
int h[N],in[N],d[N],val[N];
int vis[N];
int n,m,tot,ans,X,Y,ans1;
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void dfs(int x){
	int u;
	vis[x]=true;
	for (int i=h[x];i!=-1;i=a[i].nxt){
		u=a[i].y;
		if (vis[u]) continue;
		dfs(u);
	}
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	int x,y;
	scanf("%d%d",&n,&m);
	scanf("%d%d",&X,&Y);
	memset(h,-1,sizeof(h));
	tot=0;
	for (int i=1;i<=m;++i){
		scanf("%d%d",&x,&y);
		add(x,y);
		++in[y];
	}
	ans=1;
	for (int i=2;i<=n;++i)
		ans=1LL*ans*in[i]%MOD;
	if (X==Y){printf("%d\n",ans);return 0;}
	dfs(Y);
	if (!vis[X]){
		++in[Y];
		ans=1;
		for (int i=2;i<=n;++i) ans=1LL*ans*in[i]%MOD;
		printf("%d\n",ans);
	}
	else{
		for (int i=h[Y];i!=-1;i=a[i].nxt)
			--in[a[i].y];
		ans1=1;
		for (int i=2;i<=n;++i) ans1=1LL*ans1*in[i]%MOD;
		ans=(1LL*ans+ans1)%MOD;
		printf("%d\n",ans);
	}
}
posted @ 2018-08-23 19:13  yoyoball  阅读(190)  评论(0编辑  收藏  举报