您的位置:首页 > 其它

BZOJ4011(DP)

2016-03-04 22:29 204 查看
思路:

首先遇到问题我们如果不能一眼看出题解的话。尽量先去掉一些苛刻条件,简化问题看看会不会写 再考虑一下多了那个条件以后怎么写。

这题可以先吧多增加一条边去掉想想,在接着考虑加进来怎么办?

不会的话可以看PoPoQQQ的题解,很清楚。

/*
首先还是比较容易想到第一层
先不考虑加边的情况 这时候ans=pai(degree[i]) (2<=i<=n);
但是加边以后可能会出现不合法的情况 就是成环
这时候想办法统计出所有不合法的情况
具体来说就是sigma(所有的S(pai(degree[i])(2<=i<=n && i不属于S))) //定义S为一条y->x的路径的点集;
然后进行动态规划
f[i]表示到y->i的上式
f[i]=sigma(所有j->i的S(f[j]))/degree[i];//因为此时i的连边情况已经确定
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
using namespace std;
typedef long long LL;
const int imax=100000+229;
const int bmax=200000+229;
const int mod=1e9+7;
int n,m,y,x;
int head[imax],num,to[bmax],inext[bmax];
int degree[imax],d[imax];
LL ans,inv[bmax],f[imax];

void add(int u,int v){
to[num]=v; inext[num]=head[u]; head[u]=num++;
}
void iread()
{
scanf("%d%d%d%d",&n,&m,&x,&y);
int a,b;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b); add(a,b);
degree[b]++; d[b]++;
}
degree[y]++;
ans=1;
for(int i=2;i<=n;i++) ans=(ans*degree[i])%mod;
}

LL ipow(LL base,int k)
{
LL sans=1;
while(k)
{
if(k&1) sans=(sans*base)%mod;
base=(base*base)%mod;
k>>=1;
}
return sans;
}

void pre(){
for(int i=1;i<=m+1;i++) inv[i]=ipow(i,mod-2);
}

vector<int> q;
void iwork()
{
pre();
if(y==1) { printf("%lld\n",ans); return;}//记得加特判

for(int i=1;i<=n;i++) if(!d[i]) q.push_back(i); //由于是拓扑图 不能直接加入1,看样例
f[y]=ans; // 由于y是起始点 不需要除以他的入度
while(!q.empty())
{
int u=q.back(); q.pop_back();
f[u]=(f[u]*inv[degree[u]])%mod;
for(int i=head[u];i!=-1;i=inext[i])
{
int v=to[i];
f[v]=(f[v]+f[u])%mod;
--d[v];
if(!d[v]) q.push_back(v);
}
}
printf("%lld\n",(ans-f[x]+mod)%mod);
}

int main()
{
iread();
iwork();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp