nflsoj 20034 #12458.「NOIP2021模拟赛0929知临」棋盘

小C有一个 $n $ 列的棋盘,一开始是空的,棋盘上可能会有一些障碍无法通过

小C要对它进行了 \(q\) 次操作,每次操作为以下三种操作之一

  1. Add:在棋盘末尾加入了一行
  2. Del:删除棋盘的第一行
  3. Que: 询问:有一只马从当前棋盘的第一行第ii列出发,要到最后一行第jj列。每次只能向棋盘的下方走,询问合法的方案数\(\mod 998244353\) .

**如果棋盘为空,起点或者终点有障碍,均输出 \(0\) **

一只马 \((x,y)\) 的活动范围是 \((x+1,y-2),(x+1,y+2),(x+2,y-1),(x+2,y+1)\)

\(1\leq n\leq 20,\ q\leq 100000\)

时限 : 1s

空限 : 1024mb

这个过程特别像双端队列,于是联想到上次模拟的一题,nflsoj 20034 #10232「2019五校联考-镇海3」小 ω 的仙人掌 .

可以用两个栈来维护 \(dp\) .

第一栈维护从后往前,第二个栈维护从前往后 .

但是,这个题目还有一个问题,就是马可以跳两层,需要维护两个 \(dp\) ,第一个是 \([l,x]\) 与 \([x+1,r]\) ,第二个是 \([l,x+1]\) 与 \([x+1,r]\) .

但是,计算的时候还是会算重,需要减掉重复的部分,就是从 \(x\) 行跳到 \(x+1\) 行的方案数 .

细节有亿点多 .

时间复杂度 : \(\mathrm O(qn^2)\)

空间复杂度 : \(\mathrm O(qn^2)\)

code

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,q;
string s[100010];
int f1[22][100010][22],g1[22][100010][22];
int f2[22][100010][22],g2[22][100010][22];
inline void upd(int &x,int y){x=(x+y)%mod;}
void nx(int g[22][100010][22],string board,int l,int tmp){
	for(int i=0;i<n;i++)for(int j=0;j<n;j++){
		g[i][tmp][j]=0;
		if(board[j]=='#')continue;
		if(j+2<n&&tmp-1>=l)upd(g[i][tmp][j],g[i][tmp-1][j+2]);
		if(j-2>=0&&tmp-1>=l)upd(g[i][tmp][j],g[i][tmp-1][j-2]);
		if(j+1<n&&tmp-2>=l)upd(g[i][tmp][j],g[i][tmp-2][j+1]);
		if(j-1>=0&&tmp-2>=l)upd(g[i][tmp][j],g[i][tmp-2][j-1]);
	}
}
void pr(int f[22][100010][22],int now,int st){
	for(int i=0;i<n;i++)for(int j=0;j<n;j++)f[i][now][j]=0;
	for(int i=0;i<n;i++)if(s[now][i]=='.')f[i][now][i]=1; 
	for(int i=0;i<n;i++)for(int k=now-1;k>=st;k--)for(int j=0;j<n;j++){
		f[i][k][j]=0;
		if(s[k][j]=='#')continue;
		if(k+1<=now&&j+2<n)upd(f[i][k][j],f[i][k+1][j+2]);
		if(k+1<=now&&j-2>=0)upd(f[i][k][j],f[i][k+1][j-2]);
		if(k+2<=now&&j+1<n)upd(f[i][k][j],f[i][k+2][j+1]);
		if(k+2<=now&&j-1>=0)upd(f[i][k][j],f[i][k+2][j-1]);
	}
}
int main(){
	freopen("chess.in","r",stdin);
	freopen("chess.out","w",stdout);
	cin>>n>>q;
	int st=1,ed=0,now=0;
	for(int t=0;t<q;t++){
		string type;
		cin>>type;
		if(type[0]=='A'){
			ed++;
			cin>>s[ed];
			if(st==ed){
				for(int i=0;i<n;i++)for(int j=0;j<n;j++){
					f1[i][st][j]=g1[i][st][j]=0;
				}
				for(int i=0;i<n;i++)if(s[ed][i]=='.')f1[i][st][i]=g1[i][st][i]=1;
				now=st;
			}else{
				nx(g1,s[ed],now,ed);
				if(st<now)nx(g2,s[ed],now-1,ed);
			}
		}else if(type[0]=='D'){
			st++;
			if(st>now){
				now=ed;
				if(st>ed)continue;
				for(int i=0;i<n;i++)for(int j=0;j<n;j++)g1[i][ed][j]=0;
				for(int i=0;i<n;i++)if(s[ed][i]=='.')g1[i][ed][i]=1;
				pr(f1,now,st);
				if(now>st){
					pr(f2,now-1,st);
					for(int i=0;i<n;i++)for(int j=0;j<n;j++)g2[i][now-1][j]=0;
					for(int i=0;i<n;i++)if(s[now-1][i]=='.')g2[i][now-1][i]=1;
					nx(g2,s[ed],now-1,ed);
				}
			}	
		}else{
			int x,y;
			cin>>x>>y;
			x--;y--;
			if(st>now)cout<<"0\n";
			else if(st==now)cout<<g1[x][ed][y]<<"\n";
			else{
				int ans=0;
				for(int i=0;i<n;i++){
					upd(ans,1ll*f1[i][st][x]*g1[i][ed][y]%mod);
					upd(ans,1ll*f2[i][st][x]*g2[i][ed][y]%mod);
				}
				for(int i=0;i<n;i++){
					if(i+2<n)upd(ans,(mod-1ll*f2[i][st][x]*g1[i+2][ed][y]%mod)%mod);
					if(i-2>=0)upd(ans,(mod-1ll*f2[i][st][x]*g1[i-2][ed][y]%mod)%mod);
				}
				cout<<ans<<"\n";
			}
		}
	}
	return 0;
}    
上一篇:随机化数组


下一篇:【PAT甲级、C++】1032 Sharing (25分)