象棋(高斯消元)

题目传送门

高斯消元思维题

解法

1.

对于从点 ( i , j ) (i,j) (i,j)走出棋盘,我们发现结束的状态十分的多,不妨换个思路:

从棋盘外走到点 ( i , j ) (i,j) (i,j).

那么我们可以设计 D P DP DP:
f i , j : 走到点( i , j )的期望经过回合 f_{i,j}:走到点(i,j)的期望经过回合 fi,j:走到点(ij)的期望经过回合
那么所有在棋盘外的 f i , j = 0 f_{i,j}=0 fi,j=0
{ d x i , d y i } \{dx_i,dy_i\} {dxi,dyi}为一个方向的偏移,一共有 8 8 8种,令它们的下标从 0 0 0开始
那么就写出转移:
f i , j = ∑ k = 0 k < 8 p k ∗ f i + d x k , j + d y k f_{i,j}=\sum_{k=0}^{k<8}p_k*f_{i+dx_k,j+dy_k} fi,j=k=0k<8pkfi+dxk,j+dyk

2.

然后发现转移有环,不可直接转移,要用高斯消元求解
稍微计算一下复杂度:
一共有 n m nm nm个未知量,即 n m nm nm个方程,所以是 O ( ( n m ) 3 ) O((nm)^3) O((nm)3)
写好一点的话可以拿 60 p t s 60pts 60pts,对于 T 4 T4 T4来说已经不错了

3.

考虑优化高斯消元
一个经典的想法就是设主元,减少方程数
而发现确实如此,我们知道:
f i , j = ∑ k = 0 k < 8 p k ∗ f i + d x k , j + d y k f_{i,j}=\sum_{k=0}^{k<8}p_k*f_{i+dx_k,j+dy_k} fi,j=k=0k<8pkfi+dxk,j+dyk
那么就有:
f i + d x 0 , j + d y 0 = f i , j ∑ k = 0 ( k < 8 ) ∧ ( k ≠ 0 ) p k ∗ f i + d x k , j + d y k p 0 f i + d x 1 , j + d y 1 = f i , j ∑ k = 0 ( k < 8 ) ∧ ( k ≠ 1 ) p k ∗ f i + d x k , j + d y k p 1 . . . . . . \begin{aligned} &f_{i+dx_0,j+dy_0}=\frac{f_{i,j}\sum_{k=0}^{(k<8)\wedge(k\ne0)}p_k*f_{i+dx_k,j+dy_k} }{p_0} \\ &f_{i+dx_1,j+dy_1}=\frac{f_{i,j}\sum_{k=0}^{(k<8)\wedge(k\ne1)}p_k*f_{i+dx_k,j+dy_k} }{p_1} \\ &...... \end{aligned} fi+dx0,j+dy0=p0fi,jk=0(k<8)(k=0)pkfi+dxk,j+dykfi+dx1,j+dy1=p1fi,jk=0(k<8)(k=1)pkfi+dxk,j+dyk......
我们就根据其构造新的方程组即可
最后回代系数算出真实结果即可

code

#include
using namespace std;
typedef long long ll;
const int dx[]={2,1,-1,-2,-2,-1,1,2};
const int dy[]={1,2,2,1,-1,-2,-2,-1};
const int N=600,mod=998244353,M=2e2+7;
int a[N][N],c[N],ans[M*M],down[N];
int ljh[M][M][N];
int cnt,cnt1,n,m,sum;
int w[17],invw[17];
int qpow(int a,int b){
	int res=1; while(b){
		if(b&1) res=1ll*res*a%mod;
		a=1ll*a*a%mod,b>>=1;
	}
	return res;
}
int gett(int x,int y) { return (x-1)*m+y;}
int inv(int x){ return qpow(x,mod-2); }
bool check(int x,int y){ return (x>=1&&x<=n&&y>=1&&y<=m);}
void Gauss(int n){
	for(int i=1;i<=n;i++) {
		int k=0;
		for(int j=i;j<=n;j++) {
			if(a[j][i]) {k=j;break;}        
		}    
		if(!k) return ;
		for(int j=1;j<=n+1;j++) swap(a[i][j],a[k][j]);        
		int p=inv(a[i][i]);
		for(int j=i;j<=n+1;j++) a[i][j]=1ll*a[i][j]*p%mod;
		for(int j=1;j<=n;j++) {
			if(j==i) continue;  
			int tmp=a[j][i];
			for(int k=1;k<=n+1;k++) {
				a[j][k]-=1ll*a[i][k]*tmp%mod;;
				a[j][k]=(a[j][k]%mod+mod)%mod;          
			}        
		}    
	}
}
void work() {
	for(int i=1;i<=m;i++) {
		ljh[1][i][++cnt]=1,down[cnt]=gett(1,i);
		ljh[2][i][++cnt]=1,down[cnt]=gett(2,i);
	}
	for(int i=3;i<=n;i++) ljh[i][1][++cnt]=1,down[cnt]=gett(i,1);    
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			if(check(i+2,j+1)) {
				for(int t=1;t<=cnt+1;t++) ljh[i+2][j+1][t]=ljh[i][j][t];                
				for(int k=0;k<8;k++) {
					int nx=i-dx[k],ny=j-dy[k];
					if((k==4)||(!check(nx,ny))) continue;      
					for(int t=1;t<=cnt+1;t++){              
						ljh[i+2][j+1][t]-=1ll*ljh[nx][ny][t]*w[k]%mod;
						ljh[i+2][j+1][t]=(ljh[i+2][j+1][t]%mod+mod);
					}                
				}
				ljh[i+2][j+1][cnt+1]--;
				ljh[i+2][j+1][cnt+1]=(ljh[i+2][j+1][cnt+1]%mod+mod);
				for(int t=1;t<=cnt+1;t++) 
					ljh[i+2][j+1][t]=1ll*ljh[i+2][j+1][t]*invw[4]%mod;               
			}else {
				cnt1++;
				for(int t=1;t<=cnt+1;t++) a[cnt1][t]=ljh[i][j][t];
				for(int k=0;k<8;k++) {
					int nx=i-dx[k],ny=j-dy[k];
					if((k==4)||(!check(nx,ny))) continue;                    
					for(int t=1;t<=cnt+1;t++){   
						a[cnt1][t]-=1l*ljh[nx][ny][t]*w[k]%mod;
						a[cnt1][t]=(a[cnt1][t]%mod+mod)%mod;
					}                
				}
				a[cnt1][cnt+1]--;
				a[cnt1][cnt+1]=(a[cnt1][cnt+1]%mod+mod)%mod;
				a[cnt1][cnt+1]=-a[cnt1][cnt+1];
				a[cnt1][cnt+1]=(a[cnt1][cnt+1]%mod+mod)%mod;
			}        
		}    
	}
	Gauss(cnt);
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++) {
			for(int t=1;t<=cnt;t++) 
				ans[gett(i,j)]=(1ll*ans[gett(i,j)]+1ll*a[t][cnt+1]*ljh[i][j][t]%mod)%mod;
			ans[gett(i,j)]=(1ll*ans[gett(i,j)]+1ll*ljh[i][j][cnt+1])%mod; 
		}    
	}
}
void init(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<8;i++) {
		scanf("%d",&w[i]);
		sum+=w[i];
	}
	sum=inv(sum);
	for(int i=0;i<8;i++) {
		w[i]=1ll*w[i] *sum%mod;
		invw[i]=inv(w[i]);
	}
}
int main() {
	freopen("chess.in","r",stdin);
	freopen("chess.out","w",stdout);
	init();
	work(); 
	int Q;scanf("%d",&Q); while(Q--) {
		int x,y; scanf("%d%d",&x,&y);
		printf("%d\n",ans[gett(x,y)]);
	}
}

你可能感兴趣的:(算法,线性代数)