1.2019icpc南京网络赛D Robot
题目链接:https://nanti.jisuanke.com/t/41301
题目大意:
有一个 n n n个点 m m m条边的 D A G DAG DAG, 第 i i i步的花费是 i i i, 问 D A G DAG DAG上从 1 t h 1_{th} 1th节点到 n t h n_{th} nth节点的期望花费。
题解思路:
概率dp的题目一般都是用
d
p
[
1
]
dp[1]
dp[1]表示答案,这题同样套用概率dp的模板,用
d
p
[
i
]
dp[i]
dp[i]表示从
i
i
i号点到
n
n
n号点的期望花费。
(
p
s
:
ps:
ps:这样反向设可以避免后效性) 要反向建图。
在求
d
p
[
i
]
dp[i]
dp[i]之前,要先求出所有
i
i
i号节点的前驱,所以需要反向建完图之后跑一个拓扑排序。
再维护一个
d
a
y
[
i
]
day[i]
day[i]表示
i
i
i号点到
n
n
n号点的期望天数
转移方程就是:
d a y [ u ] = ∑ ( d a y [ v ] + 1 ) + ( d a y [ u ] + 1 ) o u t [ u ] + 1 day[u]=\frac{\sum (day[v]+1) +(day[u]+1)}{out[u]+1} day[u]=out[u]+1∑(day[v]+1)+(day[u]+1)
d p [ u ] = ∑ ( d p [ v ] + d a y [ v ] + 1 ) + ( d p [ u ] + d a y [ u ] + 1 ) o u t [ u ] + 1 dp[u]=\frac{\sum (dp[v]+day[v]+1) +(dp[u]+day[u]+1)}{out[u]+1} dp[u]=out[u]+1∑(dp[v]+day[v]+1)+(dp[u]+day[u]+1)
解释一下,在原图中,
u
u
u节点到
n
n
n的期望=所有(
v
v
v到
n
n
n的期望
+
v
+v
+v到
n
n
n的期望天数
+
1
+1
+1)
∗
*
∗转移到
v
v
v的概率
题目允许在自身停留,所有的情况就是
u
u
u的出度
+
1
+1
+1,在分子中再单独加上这种情况,最后单独提出
d
p
[
u
]
dp[u]
dp[u]就是转移方程
反向建边的话,出度改成入度就可以了
#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#define int ll
#define debug cout<<"fuck"<<endl;
#define pb push_back
#define endl '\n'
#define fi first
#define se second
#define db double
#define pii pair<int,int>
#define mp make_pair
const int mod=(int)1e9+7;
const int maxn=(int)2e5+5;
int n,m;
vector<int>v;
queue<int>q;
vector<int>edg[maxn];
int in[maxn];
int inn[maxn];
db dp[maxn];
db day[maxn];
void solve()
{
for(int i=1;i<=n;i++)
{
if(in[i]==0)q.push(i);
}
while(!q.empty())
{
int p=q.front();q.pop();
v.pb(p);
for(int i=0;i<edg[p].size();i++)
{
int y=edg[p][i];
in[y]--;
if(in[y]==0)q.push(y);
}
}
}
void init()
{
memset(dp,0,sizeof(dp));
memset(day,0,sizeof(day));
while(!q.empty())q.pop();
for(int i=0;i<=n;i++)
{
edg[i].clear();
}
v.clear();
memset(in,0,sizeof(in));
}
int t;
signed main()
{
IOS
cin>>t;
while(t--)
{
init();
cin>>n>>m;
int xx,yy;
for(int i=1;i<=m;i++)
{
cin>>xx>>yy;
edg[yy].pb(xx);
in[xx]++;
}
memcpy(inn,in,sizeof(in));
solve();
//cout<<v[0]<<' '<<v.back()<<endl;
day[n]=0,dp[n]=0;
for(auto x:v)
{
if(x!=n)
{
day[x] += 1.0 / inn[x];
dp[x] += (1.0 + day[x]) / inn[x];
}
for(auto y:edg[x])
{
day[y]+=(1.0+day[x])/inn[y];
dp[y] +=(1.0+dp[x]+day[x])/inn[y];
}
}
cout<<fixed<<setprecision(2)<<dp[1]<<endl;
}
return 0;
}
The 14-th BIT Campus Programming Contest L 旅行的意义
题目链接 https://codeforces.com/gym/102174/problem/L
题目大意
D A G DAG DAG 从1号点开始旅行,从 u u u号点有等概率向其后继转移,不同两点间的转移需要花费一天。或者至多在 u u u停留一天,问结束旅行时的期望天数
同样套用概率 d p dp dp的套路, d p [ 1 ] dp[1] dp[1]表示 1 1 1号点结束旅行的期望天数,由于是结束的期望,需要用 d f s dfs dfs来进行 d p dp dp数组的更新,先更新深度更深的期望天数,最后更新到 d p [ 1 ] dp[1] dp[1]。
需要处理的地方就是至多停留一天这个条件
转移方程:
d p [ u ] = 1 + ∑ ( d p [ v ] + 1 ) ∗ ( i n v ( n u m + 1 ) + i n v ( n u m + 1 ) ∗ i n v ( n u m ) ) dp[u]=1+\sum (dp[v]+1)*(inv(num+1)+inv(num+1)*inv(num)) dp[u]=1+∑(dp[v]+1)∗(inv(num+1)+inv(num+1)∗inv(num))
解释一下,
u
u
u到
v
v
v有两种方式转移,一个是在
u
u
u停留一天,一个是在
u
u
u停留两天。
转移完后再加上
i
n
v
(
n
u
m
+
1
)
inv(num+1)
inv(num+1)
#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#define int ll
#define debug cout<<"fuck"<<endl;
#define pb push_back
#define endl '\n'
#define fi first
#define se second
#define db double
#define pii pair<int,int>
#define mp make_pair
const int mod=(int)998244353;
const int maxn=(int)1e5+5;
int n,m,t;
int dp[maxn];
int vis[maxn];
vector<int>g[maxn];
ll qp(ll a,ll b,ll c)
{
ll res=1;
while(b)
{
if(b & 1)
{res=(res*a)%c;}
a=(a*a)%c;
b=b>>1;
}
return res;
}
int inv(int x)
{
return qp(x,mod-2,mod);
}
void dfs(int now)
{
if(vis[now])
{
return;
}
vis[now]=1;
int sz=(int)g[now].size();
for(auto nxt:g[now])
{
dfs(nxt);
dp[now] += (dp[nxt]+1) * ((inv(sz+1)+inv(sz+1)*inv(sz)%mod)%mod)%mod;
dp[now] %= mod;
}
dp[now]+=inv(sz+1);
dp[now]%=mod;
}
signed main()
{
int x,y;
IOS
cin>>t;
while(t--)
{
memset(vis,0,sizeof(vis));
cin>>n>>m;
for(int i=1;i<=n;i++)
{
dp[i]=1;
g[i].clear();
}
while(m--)
{
cin>>x>>y;
g[x].pb(y);
}
dfs(1);
cout<<dp[1]<<endl;
}
return 0;
}