高斯消元入门

高斯消元入门

最近学了一下高斯消元,其实用这个东西来解决有环的dp还是挺巧妙的。

高斯消元模板

#include<bits/stdc++.h>
using namespace std;
#define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i)
#define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i)
typedef long long ll;
inline int read(){
    int x;
    char c;
    int f=1;
    while((c=getchar())!='-' && (c<'0' || c>'9'));
    if(c=='-') c=getchar(),f=-1;
    x=c^'0';
    while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0');
    return x*f;
}
inline ll readll(){
    ll x;
    char c;
    ll f=1;
    while((c=getchar())!='-' && (c<'0' || c>'9'));
    if(c=='-') c=getchar(),f=-1;
    x=c^'0';
    while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0');
    return x*f;
}
const int maxn=100+10,eps=1e-8;
double a[maxn][maxn],ans[maxn],c[maxn];
bool check(double x){
    return fabs(x)<=eps;
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("gauss.in","r",stdin);
    freopen("gauss.out","w",stdout);
#endif
    int n=read();
    REP(i,1,n){
        REP(j,1,n) scanf("%lf",&a[i][j]);
        scanf("%lf",&c[i]);
    }
    REP(i,1,n){
        if(check(a[i][i])){
            bool flag=0;
            REP(j,i+1,n)
                if(!check(a[j][i])){
                    swap(a[i],a[j]),swap(c[i],c[j]),flag=1;
                    break;
                }
            if(!flag){
                printf("No Solution\n");
                return 0;
            }
        }
        REP(j,i+1,n){
            double x=a[j][i]/a[i][i];
            REP(k,i,n) a[j][k]-=a[i][k]*x;
            c[j]-=c[i]*x;
        }
    }
    DREP(i,n,1){
        ans[i]=c[i]/a[i][i];
        REP(j,1,i-1) c[j]-=a[j][i]*ans[i];
    }
    REP(i,1,n) printf("%.2lf\n",ans[i]);
    return 0;
}

bzoj3270: 博物馆

Description

有一天Petya和他的朋友Vasya在进行他们众多旅行中的一次旅行,他们决定去参观一座城堡博物馆。这座博物馆有着特别的样式。它包含由m条走廊连接的n间房间,并且满足可以从任何一间房间到任何一间别的房间。

两个人在博物馆里逛了一会儿后两人决定分头行动,去看各自感兴趣的艺术品。他们约定在下午六点到一间房间会合。然而他们忘记了一件重要的事:他们并没有选好在哪儿碰面。等时间到六点,他们开始在博物馆里到处乱跑来找到对方(他们没法给对方打电话因为电话漫游费是很贵的)

不过,尽管他们到处乱跑,但他们还没有看完足够的艺术品,因此他们每个人采取如下的行动方法:每一分钟做决定往哪里走,有Pi 的概率在这分钟内不去其他地方(即呆在房间不动),有1-Pi 的概率他会在相邻的房间中等可能的选择一间并沿着走廊过去。这里的i指的是当期所在房间的序号。在古代建造是一件花费非常大的事,因此每条走廊会连接两个不同的房间,并且任意两个房间至多被一条走廊连接。

两个男孩同时行动。由于走廊很暗,两人不可能在走廊碰面,不过他们可以从走廊的两个方向通行。(此外,两个男孩可以同时地穿过同一条走廊却不会相遇)两个男孩按照上述方法行动直到他们碰面为止。更进一步地说,当两个人在某个时刻选择前往同一间房间,那么他们就会在那个房间相遇。

两个男孩现在分别处在a,b两个房间,求两人在每间房间相遇的概率。

Input

第一行包含四个整数,n表示房间的个数;m表示走廊的数目;a,b (1 ≤ a, b ≤ n),表示两个男孩的初始位置。

之后m行每行包含两个整数,表示走廊所连接的两个房间。

之后n行每行一个至多精确到小数点后四位的实数 表示待在每间房间的概率。

题目保证每个房间都可以由其他任何房间通过走廊走到。

Output

输出一行包含n个由空格分隔的数字,注意最后一个数字后也有空格,第i个数字代表两个人在第i间房间碰面的概率(输出保留6位小数)

注意最后一个数字后面也有一个空格

\(f[x][y]\)代表两个人分别在x和y的概率,\(g[i]=\frac {1-p_i} {d_i}\),\(d_i\)为i号点的度数

\[f[x][y] = f[x][y] * p_i + \sum_{x->i} \sum_{y->j} f[i][j] * g[i] * g[j] + \sum_{x->i} f[i][y] * g[i] * p_i + \sum_{y->i} f[x][i] * g[i] * p_i \]

\(f[S][T]\)需要加1

则答案就是\(f[i][i]\)

#include<bits/stdc++.h>
using namespace std;
#define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i)
#define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i)
#define id(x,y) ((x-1)*n+y)
typedef long long ll;
inline int read(){
    int x;
    char c;
    int f=1;
    while((c=getchar())!='-' && (c<'0' || c>'9'));
    if(c=='-') c=getchar(),f=-1;
    x=c^'0';
    while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0');
    return x*f;
}
inline ll readll(){
    ll x;
    char c;
    ll f=1;
    while((c=getchar())!='-' && (c<'0' || c>'9'));
    if(c=='-') c=getchar(),f=-1;
    x=c^'0';
    while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0');
    return x*f;
}
const int maxn=400+10;
const double eps=1e-6;
double a[maxn][maxn],ans[maxn],c[maxn];
double p[maxn],g[maxn];
vector<int> G[maxn];
bool check(double x){
    return fabs(x)<=eps;
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("gauss.in","r",stdin);
    freopen("gauss.out","w",stdout);
#endif
    int n=read(),m=read(),S=read(),T=read();
    int N=id(n,n);
    REP(i,1,m){
        int x=read(),y=read();
        G[x].push_back(y),G[y].push_back(x);
    }
    REP(i,1,n) scanf("%lf",&p[i]);
    REP(i,1,n) g[i]=(1-p[i])/G[i].size();
    REP(i,1,n)
        REP(j,1,n){
            int u=id(i,j);
            a[u][u]=1;
            if(i!=j) a[u][u]-=p[i]*p[j];
            REP(x,0,G[i].size()-1)
                REP(y,0,G[j].size()-1)
                    if(G[i][x]!=G[j][y])
                        a[u][id(G[i][x],G[j][y])]-=g[G[i][x]]*g[G[j][y]];
            REP(x,0,G[i].size()-1)
                if(G[i][x]!=j)
                    a[u][id(G[i][x],j)]-=g[G[i][x]]*p[j];
            REP(y,0,G[j].size()-1)
                if(i!=G[j][y])
                    a[u][id(i,G[j][y])]-=p[i]*g[G[j][y]];
        }
    c[id(S,T)]=1;
    REP(i,1,N){
        if(check(a[i][i])){
            REP(j,i+1,N)
                if(!check(a[j][i])){
                    swap(a[j],a[i]),swap(c[j],c[i]);
                    break;
                }
        }
        REP(j,i+1,N){
            double u=a[j][i]/a[i][i];
            REP(k,i,N) a[j][k]-=a[i][k]*u;
            c[j]-=c[i]*u;
        }
    }
    DREP(i,N,1){
        ans[i]=c[i]/a[i][i];
        REP(j,1,i-1) c[j]-=a[j][i]*ans[i];
    }
    REP(i,1,n) printf("%.6lf ",ans[id(i,i)]);
    return 0;
}

bzoj1778: [Usaco2010 Hol]Dotp 驱逐猪猡

Description

奶牛们建立了一个随机化的臭气炸弹来驱逐猪猡。猪猡的文明包含1到N (2 <= N <= 300)一共N个猪城。这些城市由M (1 <= M <= 44,850)条由两个不同端点A_j和B_j (1 <= A_j<= N; 1 <= B_j <= N)表示的双向道路连接。保证城市1至少连接一个其它的城市。一开始臭气弹会被放在城市1。每个小时(包括第一个小时),它有P/Q (1 <= P <=1,000,000; 1 <= Q <= 1,000,000)的概率污染它所在的城市。如果这个小时内它没有污染它所在的城市,那麽它随机地选择一条道路,在这个小时内沿着这条道路走到一个新的城市。可以离开这个城市的所有道路被选择的概率均等。因为这个臭气弹的随机的性质,奶牛们很困惑哪个城市最有可能被污染。给定一个猪猡文明的地图和臭气弹在每个小时内爆炸的概率。计算每个城市最终被污染的概率。如下例,假设这个猪猡文明有两个连接在一起的城市。臭气炸弹从城市1出发,每到一个城市,它都有1/2的概率爆炸。 1--2 可知下面这些路径是炸弹可能经过的路径(最后一个城市是臭气弹爆炸的城市): 1: 1 2: 1-2 3: 1-2-1 4: 1-2-1-2 5: 1-2-1-2-1 ... 要得到炸弹在城市1终止的概率,我们可以把上面的第1,第3,第5……条路径的概率加起来,(也就是上表奇数编号的路径)。上表中第k条路径的概率正好是(1/2)^k,也就是必须在前k-1个回合离开所在城市(每次的概率为1 - 1/2 = 1/2)并且留在最后一个城市(概率为1/2)。所以在城市1结束的概率可以表示为1/2 + (1/2)^3 + (1/2)^5 + ...。当我们无限地计算把这些项一个个加起来,我们最后会恰好得到2/3,也就是我们要求的概率,大约是0.666666667。这意味着最终停留在城市2的概率为1/3,大约为0.333333333。

Input

* 第1行: 四个由空格隔开的整数: N, M, P, 和 Q * 第2到第M+1行: 第i+1行用两个由空格隔开的整数A_j和B_j表示一条道路。

Output

* 第1到第N行: 在第i行,用一个浮点数输出城市i被摧毁的概率。误差不超过\(10^{-6}\)的答案会 被接受(注意这就是说你需要至少输出6位有效数字使得答桉有效)。

首先必须要吐槽,这题竟然不开spj,只有保留9位小数才能过。

我们设f[x]代表安全到x号点的概率。

则很容易知道\(f[x]=\sum f[G[x][j]]*g[G[x][j]]+[x=1]\)

最后的答案就是\(f[x]*p\)

#include<bits/stdc++.h>
using namespace std;
#define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i)
#define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i)
typedef long long ll;
inline int read(){
    int x;
    char c;
    int f=1;
    while((c=getchar())!='-' && (c<'0' || c>'9'));
    if(c=='-') c=getchar(),f=-1;
    x=c^'0';
    while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0');
    return x*f;
}
inline ll readll(){
    ll x;
    char c;
    ll f=1;
    while((c=getchar())!='-' && (c<'0' || c>'9'));
    if(c=='-') c=getchar(),f=-1;
    x=c^'0';
    while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0');
    return x*f;
}
inline bool chkmax(int &x,int y){return (y>x)?(x=y,1):0;}
inline bool chkmin(int &x,int y){return (y<x)?(x=y,1):0;}
const int maxn=300+10;
const double eps=1e-20;
double a[maxn][maxn],c[maxn],g[maxn],ans[maxn];
vector<int> G[maxn];
inline bool check(double x){
    return fabs(x)<=eps;
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("gauss.in","r",stdin);
    freopen("gauss.out","w",stdout);
#endif
    int n=read(),m=read();
    double p=read(),q=read();
    p/=q;
    REP(i,1,m){
        int x=read(),y=read();
        G[x].push_back(y),G[y].push_back(x);
    }
    REP(i,1,n) g[i]=(1-p)/G[i].size();
    REP(i,1,n){
        a[i][i]=1;
        REP(j,0,G[i].size()-1)
            a[i][G[i][j]]-=g[G[i][j]];
    }
    c[1]=1;
    REP(i,1,n){
        if(check(a[i][i])){
            REP(j,i+1,n)
                if(!check(a[j][i])){
                    swap(a[i],a[j]),swap(c[i],c[j]);
                    break;
                }
        }
        REP(j,i+1,n){
            double u=a[j][i]/a[i][i];
            REP(k,i,n)
                a[j][k]-=a[i][k]*u;
            c[j]-=c[i]*u;
        }
    }
    DREP(i,n,1){
        if(!check(a[i][i])) ans[i]=c[i]/a[i][i];
        REP(j,1,i-1) c[j]-=a[j][i]*ans[i];
    }
//  cerr<<sum<<endl;
    REP(i,1,n) printf("%.9lf\n",ans[i]*p);
    return 0;
}

bzoj[HEOI2015]小Z的房间

Description

你突然有了一个大房子,房子里面有一些房间。事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子。在一开始的时候,相邻的格子之间都有墙隔着。

你想要打通一些相邻房间的墙,使得所有房间能够互相到达。在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙)。同时,你不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路。现在,你希望统计一共有多少种可行的方案。

Input

第一行两个数分别表示n和m。

接下来n行,每行m个字符,每个字符都会是’.’或者’’,其中’.’代表房间,’’代表柱子。

Output

一行一个整数,表示合法的方案数 Mod \(10^9\)

Matrix-Tree模板题,直接套定理,求det用高斯消元即可

#include<bits/stdc++.h>
using namespace std;
#define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i)
#define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i)
typedef long long ll;
inline int read(){
    int x;
    char c;
    int f=1;
    while((c=getchar())!='-' && (c<'0' || c>'9'));
    if(c=='-') c=getchar(),f=-1;
    x=c^'0';
    while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0');
    return x*f;
}
inline ll readll(){
    ll x;
    char c;
    ll f=1;
    while((c=getchar())!='-' && (c<'0' || c>'9'));
    if(c=='-') c=getchar(),f=-1;
    x=c^'0';
    while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0');
    return x*f;
}
inline bool chkmax(int &x,int y){return (y>x)?(x=y,1):0;}
inline bool chkmin(int &x,int y){return (y<x)?(x=y,1):0;}
const int maxn=100+10,mod=1e9;
char s[maxn][maxn];
int id[maxn][maxn],N;
int dir[4][2]={{1,0},{-1,0},{0,-1},{0,1}};
ll a[maxn][maxn];
int main(){
#ifndef ONLINE_JUDGE
    freopen("det.in","r",stdin);
    freopen("det.out","w",stdout);
#endif
    int n=read(),m=read();
    REP(i,1,n){
        scanf("%s",s[i]+1);
        REP(j,1,m)
            if(s[i][j]=='.')
                id[i][j]=++N;
    }
    REP(i,1,n)
        REP(j,1,m)
            if(s[i][j]=='.')
                REP(k,0,3){
                    int u=i+dir[k][0],v=j+dir[k][1];
                    if(u>n || v>m || u<1 || v<1) continue;
                    if(s[u][v]!='.') continue;
                    a[id[i][j]][id[u][v]]--;
                    a[id[i][j]][id[i][j]]++;
                }
    ll ans=1;
    --N;
    REP(i,1,N){
        REP(j,i+1,N){
            ll x=a[i][i],y=a[j][i];
            while(y){
                ll t=x/y;x%=y,swap(x,y);
                REP(k,i,N) a[i][k]=(a[i][k]-t*a[j][k]%mod)%mod;
                swap(a[i],a[j]);
                ans*=-1;
            }
        }
        ans=ans*a[i][i]%mod;
        if(!a[i][i]) break;
    }
    printf("%lld\n",(ans+mod)%mod);
    return 0;
}
posted @ 2018-03-08 00:38  zhou888  阅读(232)  评论(0编辑  收藏  举报