LOJ3080 「2019 集训队互测 Day 5」国际象棋

Link
暴力高消做法是trivial的。
将前\(2\)行、第\(1\)列的所有变量作为自由元。
然后从上到下、从左到右依次考虑\(x_{i,j}\)的转移式,可以发现该转移涉及到的变量只有\(x_{i+2,j+1}\)未被自由元线性表示。
那么若\((i+2,j+1)\)在棋盘内,我们就可以由该转移得到\(x_{i+2,j+1}\)关于自由元的线性表示,否则就可以得到一个关于自由元的方程。
最后得到\(O(n+m)\)个数量和自由元相等的方程,高斯消元即可解出自由元的取值。
因为所有变量都已被自由元线性表示,那么我们就可以\(O(n+m)\)地计算出一个变量的值了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using i64=long long;
using i128=__int128;
const int N=207,M=607,P=998244353,dx[]={-2,-1,1,2,2,1,-1,-2},dy[]={-1,-2,-2,-1,1,2,2,1};
int n,m,cnt,tot,deg;
int read(){int x;scanf("%d",&x);return x;}
void dec(int&a,int b){a-=b,a+=a>>31&P;}
i64 pow(i64 a,int b=P-2){i64 r=1;for(;b;b>>=1,a=a*a%P)if(b&1)r=r*a%P;return r;}
struct poly
{
    int a[M];
    poly(){memset(a,0,sizeof a);}
    int&operator[](int x){return a[x];}
    poly operator*(i64 b){poly c;for(int k=1;k<=deg;++k)c[k]=a[k]*b%P;return c;}
    void operator-=(const poly&b){for(int k=1;k<=deg;++k)dec(a[k],b.a[k]);}
}f[N][N],a[M];
int main()
{
    n=read(),m=read();i64 sum=0,p[8];
    for(int i=0;i<8;++i) sum+=(p[i]=read());
    sum=pow(sum);for(int i=0;i<8;++i) p[i]=sum*p[i]%P;
    for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(i<=2||j<=1) f[i][j][++tot]=1;
    deg=tot+1;
    for(int i=1;i<=n;++i)
	for(int j=1;j<=m;++j)
	{
	    f[i+2][j+1]=f[i][j],--f[i+2][j+1][tot+1];
	    for(int o=0,x,y;o<8;++o) if(o^4) if(x=i+dx[o],y=j+dy[o],!(x<1||x>n||y<1||y>m)) f[i+2][j+1]-=(f[x][y]*p[o]);
	    i64 inv=pow(p[4]);f[i+2][j+1]=f[i+2][j+1]*inv;
	    if(i+2>n||j+1>m) a[++cnt]=f[i+2][j+1];
	}
    for(int i=1;i<=tot;++i)
    {
	if(!a[i][i]) for(int j=i+1;j<=tot;++j) if(a[j][i]) {std::swap(a[i],a[j]);break;}
	a[i]=a[i]*pow(a[i][i]);
	for(int j=1;j<=tot;++j) if(j!=i&&a[j][i]) a[j]-=(a[i]*a[j][i]);
    }
    for(int q=read();q;--q)
    {
	int x=read(),y=read();i128 ans=f[x][y][tot+1];
	for(int k=1;k<=tot;++k) ans-=1ll*f[x][y][k]*a[k][tot+1];
	ans=(ans%P+P)%P,printf("%d\n",(int)ans);
    }
}
posted @ 2020-06-04 15:24  Shiina_Mashiro  阅读(474)  评论(1编辑  收藏  举报