bzoj2707 [SDOI2012]走迷宫

19 篇文章 0 订阅
14 篇文章 0 订阅

Description


Morenan被困在了一个迷宫里。迷宫可以视为N个点M条边的有向图,其中Morenan处于起点S,迷宫的终点设为T。可惜的是,Morenan非常的脑小,他只会从一个点出发随机沿着一条从该点出发的有向边,到达另一个点。这样,Morenan走的步数可能很长,也可能是无限,更可能到不了终点。若到不了终点,则步数视为无穷大。但你必须想方设法求出Morenan所走步数的期望值。

N<=10000
M<=1000000
保证强连通分量的大小不超过100
另外,均匀分布着40%的数据,图中没有环,也没有自环

Solution


抛开环不谈,若给定的图是dag那么递推一下就好了
现在加上环的限制,先缩点
发现环很小,那么环里面的就高斯消元,环与环之间把已经求出来的变量当成常数套进去消元就行了

码力十分不足,我大概是zhizhang选手写了2h+

Code


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <stack>
#include <vector>

#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))

const int N=40005;
const int E=2000005;

struct edge {int x,y,next;} e[E];
std:: stack <int> stack;
// std:: vector <int> rec[N];

double a[205][205],ans[N];
int in[N],_in[N],out[N],d[N];
int dfn[N],low[N],bel[N];
int queue[N],head=1,tail=0;
int ls[N],edCnt,tot;
int rec[N][205],pos[N];
bool vis[N],flag=false;

void addEdge(int x,int y) {
    e[++edCnt]=(edge) {x,y,ls[x]};
    ls[x]=edCnt; in[y]++; out[x]+=(x!=y); d[x]++;
}

void dfs(int now) {
    dfn[now]=low[now]=++tot;
    stack.push(now); vis[now]=1;
    for (int i=ls[now];i;i=e[i].next) {
        if (!dfn[e[i].y]) {
            dfs(e[i].y);
            low[now]=std:: min(low[now],low[e[i].y]);
        } else if (vis[e[i].y]) {
            low[now]=std:: min(low[now],dfn[e[i].y]);
        }
    }
    if (low[now]==dfn[now]) {
        bel[0]++;
        for (int tmp=0;tmp!=now;) {
            tmp=stack.top(); stack.pop();
            bel[tmp]=bel[0];
            vis[tmp]=0;
            rec[bel[0]][++rec[bel[0]][0]]=tmp;
        }
    }
}

void gauss(int n) {
    rep(i,1,n) {
        int l=i;
        rep(j,i+1,n) if (fabs(a[j][i])>fabs(a[l][i])) l=j;
        if (l!=i) std:: swap(a[i],a[l]);
        double tmp=a[i][i];
        rep(j,1,n+1) a[i][j]/=tmp;
        rep(j,1,n) {
            if (i!=j&&a[j][i]) {
                tmp=a[j][i];
                rep(k,1,n+1) a[j][k]-=a[i][k]*tmp;
            }
        }
    }
}

void calc(int x) {
    fill(a,0); int size=rec[x][0];
    rep(i,1,size) pos[rec[x][i]]=i;
    rep(ti,1,size) {
        int now=rec[x][ti];
        if (!d[now]) return ;
        for (int i=ls[now];i;i=e[i].next) {
            if (bel[e[i].x]==bel[e[i].y]) {
                a[pos[now]][pos[e[i].y]]+=1.0/(double)d[now];
            } else {
                a[pos[now]][size+1]-=1.0*ans[e[i].y]/(double)d[now];
            }
        }
        a[pos[now]][size+1]-=1.0;
        a[pos[now]][pos[now]]+=-1.0;
    }
    gauss(size);
    rep(i,1,size) ans[rec[x][i]]=a[i][size+1];
}

void top_sort(int n) {
    memcpy(_in,in,sizeof(in));
    rep(i,1,n) {
        if (in[i]==0) {
            queue[++tail]=i;
        }
    }
    while (head<=tail) {
        int now=queue[head++];
        rep(j,1,rec[now][0]) {
            for (int i=ls[rec[now][j]];i;i=e[i].next) {
                if (bel[e[i].x]==bel[e[i].y]) continue;
                if (!(--in[bel[e[i].y]])) {
                    queue[++tail]=bel[e[i].y];
                }
            }
        }
    }
    memcpy(in,_in,sizeof(in));
    drp(ti,n,1) {
        int now=queue[ti] ;
        calc(now);
    }
}

void tarjan(int n,int st,int ed) {
    dfs(st);
    rep(i,1,n) {
        if (i!=ed&&dfn[i]&&!out[i]) {
            puts("INF");
            flag=true;
            return ;
        }
        if (i==ed&&!dfn[i]) {
            puts("INF");
            flag=true;
            return ;
        }
    }
    fill(in,0); fill(out,0);
    rep(i,1,edCnt) {
        if (bel[e[i].x]!=bel[e[i].y]&&bel[e[i].x]&&bel[e[i].y]) {
            in[bel[e[i].y]]++;
            out[bel[e[i].x]]++;
        }
    }
}

int main(void) {
    freopen("data.in","r",stdin);
    freopen("myp.out","w",stdout);
    int n,m,st,ed; scanf("%d%d%d%d",&n,&m,&st,&ed);
    rep(i,1,m) {
        int x,y; scanf("%d%d",&x,&y);
        if (x!=ed) addEdge(x,y);
    }
    tarjan(n,st,ed);
    if (flag) return 0;
    top_sort(bel[0]);
    rep(i,1,n)
    printf("%.3lf\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值