在一个N个点(标号1~n),M条边的有向图上,一开始我在点u,每一步我会在当前点的出边中等概率的选一条走过去,求走了恰好K步后走到每个点的概率.
第一行两个正整数N,M,表示点数和边数. 接下来M行,每行两个正整数X,Y.表示一条X向Y的一条有向边(保证没有重边和自环). 接下来一个正整数Q,表示询问个数. 接下来Q行,每行两个正整数u,K,表示开始的点和步数. N≤50,M≤1000,Q≤20,u≤n,K≤109. 每个点保证至少有一个出边.
Q行,每行N个数字,用空格隔开,第i个数字表示从u开始走K步到i的概率. 考虑到输出的答案可能会有精度问题,经过一定的分析后可以发现答案一定可以被表示成YX的形式,你只需输出X×Y109+5 mod (109+7)的值即可. 在每行后面多输出一个空格,否则可能会使你PE.
3 2 1 2 1 3 1 1 1
0 500000004 500000004
这是一个三个点,两条边的有向图,它们分别是(1−>2,1−>3).现在在1号点,走了一步后,有1/2的概率走到了2,有1/2的概率走到了3,本来应该输出 0 0.5 0.5 而根据上面所说的,应输出1∗2109+5 mod (109+7)=500000004.
因为t很大,而且发现每次的转移都是相同的,所以直接矩乘就好了.跟cf上的一题很相似
#include<bits/stdc++.h> using namespace std; const int MOD=1e9+7; int n; struct Matrix{ __int64 m[51][51]; void init(){ memset(m,0,sizeof(m)); } Matrix operator *(const Matrix &b) const{ Matrix ret; ret.init(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ if(m[i][j]!=0){ for(int k=1;k<=n;k++) ret.m[i][k]=(ret.m[i][k]+m[i][j]*b.m[j][k])%MOD; } } return ret; } }; Matrix mat[35],base; __int64 a[51]; Matrix Pow(int n,int num){ Matrix ans; ans.init(); Matrix base1; for(int i=1;i<=num;i++){ ans.m[i][i]=1; for(int j=1;j<=num;j++) base1.m[i][j]=base.m[i][j]; } int cnt=0; while(n){ if(n&1) ans=base1*ans; //不能颠倒顺序,因为乘法不满足交换律 n>>=1; base1=base1*base1; } return ans; } __int64 POW(__int64 n,int m){ __int64 ans=1; while(m){ if(m&1) ans=ans*n%MOD; n=n*n%MOD; m>>=1; } return ans; } int u[1001],v[1001]; int degree[51]; int main(){ for(int i=1;i<=50;i++) a[i]=POW((__int64)i,1000000005); int m; while(scanf("%d%d",&n,&m)!=EOF){ base.init(); memset(degree,0,sizeof(degree)); for(int i=1;i<=m;i++){ scanf("%d%d",&u[i],&v[i]); degree[u[i]]++; } for(int i=1;i<=m;i++) base.m[u[i]][v[i]]=a[degree[u[i]]]; int Q; scanf("%d",&Q); int s,k; while(Q--){ scanf("%d%d",&s,&k); Matrix ans=Pow(k,n); for(int i=1;i<=n;i++) printf("%I64d ",ans.m[s][i]); printf("\n"); } } return 0; }