1013 Div3 F题目加注释

https://codeforces.com/contest/2091/problem/F


这题主题思路就是递推,从下往上递推,然后用差分和前缀合得到下一行可能性,详细看代码注释

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int MOD = 998244353;//模数
const int N = 2222;//图的最大范围
int dp[N][N][2];//dp每个点有两种状态
string str[N];

void solve(){
	
	int n,m,d;cin>>n>>m>>d;//获取范围和最大距离
	for(int i = 1;i<=n;++i){
		for(int j = 1;j<=m;++j){
			dp[i][j][0] = dp[i][j][1] = 0;//初始化dp
		}
	}
	
	for(int i = n;i>=1;--i){
		string s;
		cin>>s;
		str[i] = "0"+s;//存储从下到上的点,然后位置从1开始
	}
	
	for(int j = 1;j <= m;++j){//遍历最下面的一行,找到可攀岩点记录为1
		if(str[1][j]=='X')
			dp[1][j][0]=1;//第一层每个点有攀岩的地方初始为一
	}
	
	for(int i = 1;i<=n;++i){	//从上到下开始每行遍历
		for(int j = 1;j<=m;++j){
			if(str[i][j]=='#')continue;//如果是不可攀岩点就跳过
			int l = max(1,j-d),r = min(m,j+d);//l找欧拉距离内最左边的点,r同理找最右点,为当前点最左和最右
			dp[i][l][1] = (dp[i][l][1] + dp[i][j][0])%MOD;//该点最左边出点为该入点的可能性和(这里用差分)
			dp[i][r+1][1] = (dp[i][r+1][1] - dp[i][j][0] + MOD)%MOD;//该点最右边出点为该入点的可能性和
		}
		for(int j = 1;j <= m;++j)dp[i][j][1] = (dp[i][j][1] + dp[i][j-1][1])%MOD;//前缀和
		if(i==n)continue;//如果到最后一行就跳过
		for(int j = 1;j <= m;++j){//再遍历当前行的每个点
			int len = d*d-1;//欧拉距离*欧拉距离-1
			len = sqrt(len);//长度的平方。(这里是勾股定理,d是斜边)
			int l = max(1,j-len),r = min(m,j+len);//获取该点最左边和最右边
			if(str[i][j]=='#')continue;//如果没攀点就跳过可能性的合并
			dp[i+1][l][0] = (dp[i+1][l][0] + dp[i][j][1])%MOD;//该点最左边的距离的下一层加上这个点的可能性(差分)
			dp[i+1][r+1][0] = (dp[i+1][r+1][0] - dp[i][j][1] + MOD)%MOD;//最右边的点减去这个点的可能性(差分)
		}
		for(int j = 1;j<=m;++j)dp[i+1][j][0] = (dp[i+1][j][0] + dp[i+1][j-1][0])%MOD;//前缀和合并下一层可能性
	}
	ll ans = 0;
	for(int j = 1;j<=m;++j){
		if(str[n][j] == 'X')ans = (ans + dp[n][j][1])%MOD;//有最后一层有盘点的加入可能性里面
	}
	cout<<ans<<endl;
	//总结,主体思路用差分来更新欧拉距离最左和最右端,然后前缀和得到每层可能性(先不管他有没有攀点)最后再遍历最上面
	//判断没有攀点的就合并可能性
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	ll t=1;cin>>t;

	while(t--){
		
		solve();
	}
	return 0;
}
posted @ 2025-03-27 22:27  我与地坛  阅读(51)  评论(0)    收藏  举报