loj2550/洛谷P4558/bzoj5318 「JSOI2018」机器人 性质分析+DP

题目分析

首先,同一根对角线上的行为决策必须一样(要么都往右要么都往下)。
灵魂画手litble
行走是循环的,走到最右就变成最左,走到最左就变成最右了,所以“一根对角线”的意义也是循环的,比如说下图每一种颜色的点都属于同一根对角线。
灵魂画手litble
假设有一根对角线,对于它上面的每一个点 ( x , y ) (x,y) (x,y) x + y = k x+y=k x+y=k。当 ( x , y ) (x,y) (x,y)出了边界后,为了使它拥有合法的意义,需要( x + = n x+=n x+=n x − = n x-=n x=n y + = m y+=m y+=m y − = m y-=m y=m),不难发现,最后所有满足 x + y = k + a n + b m x+y=k+an+bm x+y=k+an+bm a , b a,b a,b为整数)的点都在这根对角线上。根据裴蜀定理,设 d = g c d ( n , m ) d=gcd(n,m) d=gcd(n,m),所有满足 x + y = k + t d x+y=k+td x+y=k+td d d d为整数)的点都在这根对角线上。因此,一共有 d d d条不同的对角线。

而机器人每走一步,都一定会到达一根新的对角线,所以机器人的行走决策,存在长度为 d d d的循环节。

假设对于每个 d d d循环节,选择“向下走”走了 d x dx dx步,“向右走”走了 d y dy dy步。显然要 g c d ( d x , n ) = 1 gcd(dx,n)=1 gcd(dx,n)=1,才能到达每一行。要 g c d ( d y , m ) = 1 gcd(dy,m)=1 gcd(dy,m)=1,才能到达每一列。至于充分性……呃,意会(捂脸)……

枚举 d x dx dx d y dy dy也已知了,接下来的任务就只有确定循环内的决策了。

假设钦定机器人是在第 P P P步决策撞到障碍的,假设我钦定了前 P P P步决策是向下走 x x x步,向右走 y y y步,那么它撞到的障碍物的坐标应该被写成 ( 1 + x + k × d x , 1 + y + k × d y ) (1+x+k \times dx,1+y+k \times dy) (1+x+k×dx,1+y+k×dy)的形式,且 k k k要尽可能小。于是,设 ( 1 + x , 1 + y ) (1+x,1+y) (1+x,1+y)点的“权值”为 x + y + k × d x+y+k \times d x+y+k×d,即钦定在这步决策导致撞障碍物情况下,最早撞障碍物的时间。

接下来开始DP决策,设 f ( i , j , k ) f(i,j,k) f(i,j,k)表示从 ( 1 , 1 ) (1,1) (1,1)走到 ( i , j ) (i,j) (i,j)这个点(即钦定走了 i − 1 i-1 i1步向下, j − 1 j-1 j1步向右),走到的最小权值为 k k k(即已经钦定好了的决策导致最早的撞墙时间)的方案数。DP转移就是选择向下还是向右,最后将所有的 k × f ( d x + 1 , d y + 1 , k ) k \times f(dx+1,dy+1,k) k×f(dx+1,dy+1,k)加入答案中。

复杂度分析: k ≤ n ∗ m k \leq n*m knm,DP复杂度 O ( n 4 ) O(n^4) O(n4),枚举 d x dx dx复杂度 O ( n ) O(n) O(n),计算权值复杂度 O ( n 4 ) O(n^4) O(n4),总复杂度 O ( n 5 ) O(n^5) O(n5)

代码

#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int mod=998244353;
int T,n,m,d,ans,f[52][52][2502],val[52][52];
char mp[52][52];

int qm(int x) {return x>=mod?x-mod:x;}
int gcd(int x,int y) {return y?gcd(y,x%y):x;}
int DP(int dx,int dy) {
	for(RI i=1;i<=dx+1;++i)
		for(RI j=1;j<=dy+1;++j)
			for(RI k=1;k<=n*m;++k) f[i][j][k]=0;
	f[1][1][val[1][1]]=1;
	for(RI i=1;i<=dx+1;++i)
		for(RI j=1;j<=dy+1;++j) 
			for(RI k=1;k<=n*m;++k) {
				if(!f[i][j][k]) continue;
				if(i<=dx) f[i+1][j][min(k,val[i+1][j])]=
					qm(f[i+1][j][min(k,val[i+1][j])]+f[i][j][k]);
				if(j<=dy) f[i][j+1][min(k,val[i][j+1])]=
					qm(f[i][j+1][min(k,val[i][j+1])]+f[i][j][k]);
			}
	int re=0;
	for(RI i=1;i<=n*m;++i) re=qm(re+1LL*i*f[dx+1][dy+1][i]%mod);
	return re;
}

int main()
{
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d",&n,&m),d=gcd(n,m),ans=0;
		for(RI i=1;i<=n;++i) scanf("%s",mp[i]+1);
		for(RI dx=1;dx<=d;++dx) {
			if(gcd(dx,n)!=1||gcd(d-dx,m)!=1) continue;
			for(RI x=1;x<=dx+1;++x)
				for(RI y=1;y<=d-dx+1;++y) {
					int tx=x,ty=y,nowd=x-1+y-1;val[x][y]=n*m;
					do {
						if(mp[tx][ty]=='1') {val[x][y]=nowd;break;}
						tx+=dx,ty+=d-dx,nowd+=d;
						if(tx>n) tx-=n; if(ty>m) ty-=m;
					}while(tx!=x||ty!=y);
				}
			ans=qm(ans+DP(dx,d-dx));
		}
		printf("%d\n",ans);
	}
	return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值