【CF446D】DZY Loves Games(games)

Description

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

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

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

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

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

Input

输入第一行包含三个整数n,m,k(2 <=n <=500; 1 <= m <=10^5; 2 <= k <= 10^9)。

第二行包含n 个整数,它们每个都是0 或者1。如果第i 个整数为1,那么第i号房间有陷阱,否则它没有陷阱。请注意,有陷阱的房间的数量不会超过101。

保证第一房间没有陷阱,第n 号房间有陷阱。

接下来m 行,每行包含两个整数ui,vi(1 <=ui,vi<=n; ui<>vi),意味着现在的这条走廊连接的两个房间是ui 和vi。保证输入的这个走廊系统是连通的。

Output

输出唯一一个实数——DZY 开启福利关卡的机率。你的答案当它与标准答案的相对或绝对误差不超过10^-4 时被认为是正确的。

Sample Input

输入1:

5 5 3

0 0 1 0 1

1 2

2 3

3 4

4 5

1 2

输入2:

3 2 2

0 1 1

1 2

2 3

输入3:

2 1 3

0 1

1 2

Sample Output

输出1:

0.25000000

输出2:

-0.00000000

输出3:

1.00000000

Data Constraint

对于30% 的数据,n <=10

对于100%的数据,2 <=n <=500; 1 <= m <=10^5; 2 <= k <= 10^9

一开始其实一眼就看出来是高斯消元,模型是比较明显的。
一开始觉得直接dp,然后列公式,可以把式子移项一下,但是发现会n^4,然后就GG了,不知道该咋做,还有个k<=10^9。。感觉是矩阵乘法乱搞一下。
然后比赛就弃了这题。。

分析:。。为什么可以用高斯消元做呢。。首先我们要知道用高斯消元求解什么东西。
求解一个w(u,v)表示从房间u到房间v,经过的所有房间都无陷阱的概率,不包括u,v。
这个明显很简单,但是对于每个u,明显时间复杂度就是高斯消元n^3*n..这会炸。
但是我们通过仔细的思考可以发现,方程组的变量是不变的,只要每次重新计算常数项,就能做到n^3的时间复杂度,然后直接对着w跑矩阵乘法,跑k-2次,然后答案就是就是所有w相乘。
具体细节看代码。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int n,m;
const int N=605;
typedef double matrix[N][N];
int k,flag[N],bz[N],cnt,d[N],e[N][N];
double w[N][N],w1[N<<1][N<<1],ans;
inline void gauss_elimination()
{
    memset(w1,0,sizeof(w1));//w1是辅助数组,表示从i到j经过路径绝对没有陷阱的概率(包括i,j,这个和w不同)
    fo(i,1,n)
    {
        w1[i][i]=1.0;
        w1[i][i+n]=1.0;
        fo(j,1,n)
        if (!flag[j]&&j!=i)w1[i][j]=-e[i][j]/(double)d[j];//去掉陷阱,直接在前面搞个负号,到时候会自己减。
    }
    fo(i,1,n)//高斯消元
    {
        double now=w1[i][i];
        fo(j,1,2*n)w1[i][j]/=now;
        fo(j,1,n)
        if (j!=i)
        {
            double tmp=w1[j][i];
            fo(k,1,2*n)w1[j][k]-=tmp*w1[i][k];
        }
    }
    fo(i,1,cnt)
    fo(j,1,cnt)
    fo(k,1,n)
    w[j][i]+=w1[bz[i]][k+n]*(e[k][bz[j]]/(double)d[bz[j]]);
}
inline void mul(matrix &a,matrix b)
{
    static matrix c;
    memset(c,0,sizeof(c));
    fo(k,1,cnt)
    fo(i,1,cnt)
    fo(j,1,cnt)c[i][j]+=a[i][k]*b[k][j];
    memcpy(a,c,sizeof(a));
}
inline void pow(matrix a,int b)
{
    static matrix r,base;
    memcpy(base,a,sizeof(base));
    fo(i,1,cnt)r[i][i]=1;
    while (b)
    {
        if (b&1)mul(r,base);
        mul(base,base);
        b>>=1;
    }
    memcpy(w,r,sizeof(w));
}
int main()
{
    freopen("games.in","r",stdin);
    freopen("games.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    fo(i,1,n)
    {
        scanf("%d",&flag[i]);
        if (flag[i])bz[++cnt]=i;
    }
    fo(i,1,m)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        d[x]++,d[y]++;
        e[x][y]++,e[y][x]++;
    }
    gauss_elimination();
    k-=2;
    pow(w,k);
    fo(i,1,cnt)ans+=w1[bz[i]][1+n]*w[i][cnt]; 
    printf("%.10lf\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值