【jzoj3748】【CF446D】【DZY Loves Games】【矩阵乘法】

题目大意

今天DZY 想要玩一个古老的游戏。他在一个有n 个房间并有m 个走廊互相连接的大迷宫里(每个走廊都允许双向通行)。你可以认为所有房间都被走廊直接或间接连接。

DZY 在迷宫里迷路了。现在他在第一房间并且有k 条命。他将会按如下所述行动:

首先,他会随机抽取一条从他现在所处房间出发的走廊。每个抽取范围内的走廊选中的机率相等。

然后他会沿着走廊走到走廊的另一端,并且回到第一步重复这个过程。

迷宫中的一些房间里面埋着陷阱。第一房间明显没有陷阱,第n 号房间明确地有一个陷阱。每次DZY 进入这些有陷阱的房间,他都会失去一条命。现在,DZY 知道如果他恰好有两条命时进入了第n 号房间,那么首先他会失去一条命,但是然后他会开启一个福利关卡。他想要知道他开启福利关卡的机率到底为多少。请帮助他。

解题思路

求出w[i][j]表示黑点(有陷阱的点)i到黑点j的概率,将白点连在一起考虑,可以很快算出w。接下来就可以用矩阵乘法求出解决问题。

code

#include
#include
#include
#include
#include
#define LD double
#define LL long long
#define ULL unsigned long long
#define min(a,b) ((a
#define max(a,b) ((a>b)?a:b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
using namespace std;
int const mn=500+9,mm=2*1e5+9,inf=1e9;
int t,n,m,K,cnt,cnt2,gra,begin[509],to[mm],next[mm],num[1009],
    map[509][509],du[1009],a[509];
LD w[109][109],ans[109][109],tmp[109][109];
void insert(int u,int v){
    to[++gra]=v;
    next[gra]=begin[u];
    begin[u]=gra;
}
void dfs(int now,int bel){
    num[now]=bel;
    fr(i,now)if((!num[to[i]])&&(!a[to[i]]))dfs(to[i],bel);
}
void multiansw(){
    fo(i,1,cnt2)fo(j,1,cnt2)tmp[i][j]=0;
    fo(i,1,cnt2)fo(k,1,cnt2)fo(j,1,cnt2)tmp[i][k]+=ans[i][j]*w[j][k];
    fo(i,1,cnt2)fo(j,1,cnt2)ans[i][j]=tmp[i][j];
}
void multiww(){
    fo(i,1,cnt2)fo(j,1,cnt2)tmp[i][j]=0;
    fo(i,1,cnt2)fo(k,1,cnt2)fo(j,1,cnt2)tmp[i][k]+=w[i][j]*w[j][k];
    fo(i,1,cnt2)fo(j,1,cnt2)w[i][j]=tmp[i][j];
}
int main(){
    //freopen("fox.in","r",stdin);
    //freopen("fox.out","w",stdout);
    freopen("games.in","r",stdin);
    freopen("games.out","w",stdout);
    scanf("%d%d%d",&n,&m,&K);K-=2;
    fo(i,1,n)scanf("%d",&a[i]);
    int u,v;
    fo(i,1,m){
        scanf("%d%d",&u,&v);
        insert(u,v);
        insert(v,u);
    }
    cnt=n,cnt2=0;
    fo(i,1,n)if(!a[i]){
        if(!num[i])cnt++,dfs(i,cnt);
    }else num[i]=++cnt2;
    fo(i,1,n)if(a[i])fr(j,i){
        du[i]++;
        if(!a[to[j]])du[num[to[j]]]++,map[i][num[to[j]]-n]++;
    }
    fo(i,1,n)if(a[i]){
        fr(j,i)if(a[to[j]])w[num[i]][num[to[j]]]+=1.0/du[i];
        fo(j,1,n)if(a[j])fo(k,1,cnt-n)
            w[num[i]][num[j]]+=1.0*map[i][k]/du[i]*map[j][k]/du[k+n];
    }
    fo(i,1,n)if(a[i]&&map[i][num[1]]-n)
        ans[1][num[i]]=1.0*map[i][num[1]-n]/du[num[1]];
    while(K){
        if(K&1)multiansw();
        multiww();
        K>>=1;
    }
    printf("%lf\n",ans[1][num[n]]);
    return 0;
}

你可能感兴趣的:(jzoj,矩阵乘法)