蓝桥杯 观光铁路

版权声明:原来这里可以拿来卖萌ヽ(・∀・)ノ https://blog.csdn.net/u012345506/article/details/80384715

考虑用递推解决,设 f a ( v , q ) 表示当前走到 v 节点,上一次到达的是 q 节点,最终回到 a 节点的路径长度的期望。
容易得: f a ( v , q ) = 1 + 1 | o u t v | [ v q ] x n e x t v x q f a ( x , v )

注意到这个方程式是不能直接递推的,将 f a 项移到等号左侧,得到一个 n 2 元线性方程组,那么我们就可以用高斯消元解出分别以 f 1 , f 2 , f 3 . . . f n 为未知数的 n 个方程组。这么做需要的复杂度为 O ( ( n 2 ) 3 n ) = O ( n 7 ) ,考虑优化。

仔细观察我们得到的这个线性方程组,为分析方便,我们将二元组 ( v , q ) 映射为一个整数 ( v , q ) ,将由 f a ( v , q ) 的递推式产生的方程放置于矩阵的第 ( v , q ) 行。显然的,有:

M ( v , q ) , ( v , q ) = 1
M ( v , q ) , x 0 | x n e x t v
其余项皆为 0

考虑在消元时,矩阵中非 0 项的变化。我们将 ( v , q ) v 相同的方程称为一组,显然的,不同组的出边产生的非 0 项的位置交集为空,且在消元时同一组内的方程必然不可能产生交互。可以发现,每次完成对一个组的消元,我们的矩阵中将有 O ( n ) 个列变得非 0 ,且这 O ( n ) 个列在区间 [ i n + 1 , ( i + 1 ) n ] | i [ 0 , n 1 ] 中各有一个,所以对于第 i 个进行消元的组,将会有 n i + 1 个方程的行变换次数为 O ( n ) ,其余的方程行变换次数为 n 2 ( i 1 ) n 。前者产生的总行变换次数为 O ( n 3 ) ,后者的次数需要仔细计算:
i = 2 n ( i 1 ) ( n 2 ( i 1 ) n )
= i = 1 n 1 i ( n 2 i n )
= i = 1 n 1 i n 2 i 2 n
= n 4 n 2 6

所以实际上在 n = 21 时,一次消元产生的总行变换次数只有 4 10 4 次左右。那么总复杂度为 O ( 40000 n 3 ) ,实际的数字会更小一些。

因为垃圾蓝桥OJ没有对这题使用SPJ,所以以下代码只能拿65分(题目上已经标明误差范围但OJ没有使用SPJ判断),懒得再优化精度了。只能说:

珍爱生命,远离蓝翔杯。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))
typedef long long ll;
const int up=21;const double eps=1e-9;
struct eg{int u,v,nx;}gp[10010];int cnt,hd[up+10],n,m,od[up+10];double mt[900][900],tm[900][900];
inline void psh(int u,int v){++cnt;gp[cnt].u=u,gp[cnt].v=v,gp[cnt].nx=hd[u],hd[u]=cnt;};
inline int _btn(int a,int b){return a*n+b;};
inline int _seg(int a,int b){return a==b?0:1;};
inline bool _dmp(double a,double b){return fabs(b-a)<=eps;};
inline void _swp(int a,int b,int n){int i,j;for(i=0;i<=n;++i)swap(mt[a][i],mt[b][i]);};
inline void _ppt(int a,int b,int n,double x){int i,j;for(i=0;i<=n;++i)mt[b][i]-=x*mt[a][i];};
void guess(int n){
    int i,j,k,d;double xq;for(i=0;i<n;++i){
        for(k=i+1;k<n;++k)if(!_dmp(mt[k][i],0.))_ppt(i,k,n,mt[k][i]/mt[i][i]);
    }
    for(i=n-1;i>=0;--i){
        for(j=n-1;j>i;--j)mt[i][n]-=mt[j][n]*mt[i][j];
        if(!_dmp(mt[i][i],0.))mt[i][n]/=mt[i][i];
    }
};
void _initi(int d,int n){
    int i,j,t,z=sqrt(n);for(i=0;i<n;++i)for(j=0;j<=n;++j)mt[i][j]=tm[i][j];
    for(i=0;i<z;++i)if(i!=d){
        for(t=_btn(d,i),j=0;j<=n;mt[t][j++]=0.);mt[t][t]=1.0;
    }
};

void cl(){
    int i,j,k,d,t,a,b;double xq;scanf("%d %d",&n,&m);for(clr(hd),cnt=0,clr(od),i=0;i<m;++i){
        scanf("%d %d",&a,&b);--a,--b;psh(a,b),psh(b,a);od[a]++,od[b]++;
    }
    int dn=n*n;for(i=0;i<dn;++i)for(j=0;j<dn;mt[i][j++]=0.);
    for(k=i=0;i<n;++i)for(j=0;j<n;++j,++k){
        mt[k][k]=1.0,xq=1.0/(double)(od[i]-_seg(i,j));
        for(t=hd[i];t;t=gp[t].nx)if(gp[t].v!=j){
            mt[k][_btn(gp[t].v,i)]=-xq,mt[k][dn]=1.0;
        }
    }
    for(i=0;i<dn;++i)for(j=0;j<=dn;++j)tm[i][j]=mt[i][j];
    for(i=0;i<n;++i){
        _initi(i,dn);guess(dn);printf("%.12lf\n",mt[_btn(i,i)][dn]);
    }
};

int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    cl();
    return 0;
};

猜你喜欢

转载自blog.csdn.net/u012345506/article/details/80384715
今日推荐