hdu5607graph

graph

 
 
 Time Limit: 8000/4000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
问题描述
在一个NN个点(标号11~nn),MM条边的有向图上,一开始我在点uu,每一步我会在当前点的出边中等概率的选一条走过去,求走了恰好KK步后走到每个点的概率.
输入描述
第一行两个正整数N,MN,M,表示点数和边数.
接下来MM行,每行两个正整数X,YX,Y.表示一条XXYY的一条有向边(保证没有重边和自环).
接下来一个正整数QQ,表示询问个数.
接下来QQ行,每行两个正整数u,Ku,K,表示开始的点和步数.
N \leq 50, M \leq 1000, Q \leq 20, u \leq n, K \leq 10^{9}N50,M1000,Q20,un,K109.
每个点保证至少有一个出边.
输出描述
QQ行,每行NN个数字,用空格隔开,第ii个数字表示从uu开始走KK步到ii的概率.
考虑到输出的答案可能会有精度问题,经过一定的分析后可以发现答案一定可以被表示成\frac{X}{Y}YX的形式,你只需输出X \times Y^{10^9+5} \ mod \ (10^9+7)X×Y109+5 mod (109+7)的值即可.

在每行后面多输出一个空格,否则可能会使你PE.
输入样例
3 2
1 2
1 3
1
1 1
输出样例
0 500000004 500000004 
Hint
这是一个三个点,两条边的有向图,它们分别是(1->2,1->3)(1>2,1>3).现在在1号点,走了一步后,有1/2的概率走到了2,有1/2的概率走到了3,本来应该输出 0 0.5 0.5
而根据上面所说的,应输出1*2^{10^9+5} \ mod \ (10^9+7)=50000000412109+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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值