zoukankan      html  css  js  c++  java
  • 题解 2019 集训队互测 Day 5 国际象棋

    题解 2019 集训队互测 Day 5 国际象棋

    题面

    loj

    解析

    首先考虑一个 (O(mn)^3) 的高斯消元做法.

    为了方便讲,棋盘里的每个格子都有一个编号.

    考虑某个点 (i),设它下一步能走到的点为 (p_j,jin [0,7]).

    (这里的编号是用的 (0sim 7))

    设它走出棋盘的概率为 (f_i),则有:

    (f_i=sumlimits_{j=0}^{7}w_jf_{p_j}+1)

    表示下一步可能走到的某个点的步数再加上这一步,

    那么移项后有:(f_i-sumlimits_{j=0}^{7}w_jf_{p_j}=1).

    (f_i) 作为变量,则有 (n imes m) 个方程,高斯消元即可.

    但是问题是变量太多了.

    考虑把前两行和第一列的 (f_i) 作为变量,

    通过画图可以发现位置在 ((3,2)) 的格子的 (f) 可以通过前面设定的变量表示出来.

    继续类推,所有格子的 (f) 都可以通过设定的那些变量表现出来.

    并且可以发现,((3,2)) 是由 ((1,1)) 通过(frac{w_4}{P}) 的概率走到的((w) 是从 (0) 开始的)

    那么我们可以通过 (f_i) 及其它的 (7) 项推出 (f_{p_4}).

    即:(f_{p_4}=frac{f_i-sumlimits_{j=0&j!=4}^{7}w_jf_{p_j}-1}{w_4})

    而要是 (p_4) 在棋盘外面,则期望步数 (f_{p_4}=0),

    就可以得到一个关于设定的变量的方程了.

    然后高斯消元即可,复杂度 (O(n+m)^3).

    询问时把变量带进式子里即可.

    code

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define ll long long
    using namespace std;
    
    inline int read(){
    	int sum=0,f=1;char c=getchar();
    	while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
    	while(c<='9'&&c>='0'){sum=sum*10+c-'0';c=getchar();}
    	return f*sum;
    }
    
    const int N=205;
    const int Mod=998244353;
    int n,m;
    ll a[N*3][N*3],f[N][N][N*3],w[N];
    int dx[8]={-2,-1,1,2,2,1,-1,-2};
    int dy[8]={-1,-2,-2,-1,1,2,2,1};
    
    inline ll fpow(ll a,ll b){
    	ll ret=1;
    	while(b){if(b&1) ret=ret*a%Mod;a=a*a%Mod;b>>=1;}
    	return ret;
    }
    
    inline void guass(int cnt){
    	for(int i=1;i<=cnt;i++){
    		int p=-1;
    		for(int j=i;j<=cnt;j++)
    			if(a[j][i]){p=j;break;}
    		for(int j=1;j<=cnt+1;j++)
    			swap(a[i][j],a[p][j]);
    		ll inv=fpow(a[i][i],Mod-2);
    		for(int j=1;j<=cnt+1;j++) a[i][j]=a[i][j]*inv%Mod;
    		for(int j=1;j<=cnt;j++){
    			if(i==j||!a[j][i]) continue;
    			ll x=a[j][i];
    			for(int k=1;k<=cnt+1;k++) a[j][k]=(a[j][k]-a[i][k]*x%Mod+Mod)%Mod;
    		}
    	}
    }
    
    inline void Add(ll &x,ll y){
    	x=(x+y)%Mod;x+=Mod;
    	if(x>Mod) x-=Mod;
    }
    
    inline void Mul(ll &x,ll y){x=x*y%Mod;}
    
    signed main(){
    	n=read();m=read();
    	for(int i=0;i<8;i++) w[i]=read();
    	ll P=0;
    	for(int i=0;i<8;i++) P=(P+w[i])%Mod;
    	P=fpow(P,Mod-2);
    	for(int i=0;i<8;i++) w[i]=1ll*w[i]*P%Mod;
    	int tot=0,tt=0;
    	for(int i=1;i<=2;i++)
    		for(int j=1;j<=m;j++) f[i][j][++tot]=1;
    	for(int i=3;i<=n;i++) f[i][1][++tot]=1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			int x=i+2,y=j+1;
    			for(int k=1;k<=tot+1;k++) f[x][y][k]=f[i][j][k];
    			f[x][y][tot+1]++;
    			for(int k=0;k<8;k++){
    				if(k==4) continue;
    				int i1=i+dx[k],j1=j+dy[k];
    				if(i1<1||j1<1||i1>n||j1>m) continue;
    				for(int l=1;l<=tot+1;l++) Add(f[x][y][l],-f[i1][j1][l]*w[k]%Mod);
    			}
    			ll inv=fpow(w[4],Mod-2);
    			for(int k=1;k<=tot+1;k++) Mul(f[x][y][k],inv);
    			if(x>n||y>m){
    				tt++;
    				for(int k=1;k<=tot+1;k++) a[tt][k]=f[x][y][k];
    			}
    		}
    	guass(tot);
    	int Q=read();
    	while(Q--){
    		int x=read(),y=read();
    		ll ans=-f[x][y][tot+1];
    		for(int i=1;i<=tt;i++) Add(ans,f[x][y][i]*a[i][tot+1]);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    composer的使用
    tp5短信接口的使用
    PHP序列化与反序列化
    PHP 的oop思想
    php单例模式
    统计图的使用(chart)
    jq的时间插件
    php中Excel操作
    Linux 常用命令
    think cmfx目录结构
  • 原文地址:https://www.cnblogs.com/permzf/p/13046810.html
Copyright © 2011-2022 走看看