2019.03.08【JSOI2018】【洛谷P4558】【BZOJ5318】掃地機器人(哈密頓路徑)(裴蜀定理)(DP)

洛谷傳送門

BZOJ傳送門


解析:

首先我們給每個格子一個決策方向,向下或向右。可以很輕易的發現,如果想要合法,所有循環對角線的格子必須要決策相同,不然的話會有格子無法被達到。

於是我們發現在所有對角線的決策相同的時候,機器人的路徑是一個循環,且循環節長度爲gcd(n,m)\gcd(n,m),而且合法的循環節長度只能爲gcd(n,m)\gcd(n,m),由裴蜀定理,單次循環中向下和向右的長度也必須和循環節長度和當前維度長度互質。

同時通過感性理解 我們可以發現所有這樣的解都是合法的。

那麼我們考慮枚舉單個循環中向下和向右的方案數,然後對合法的計算答案。

顯然我們在走過tt個循環的時候,爲了轉移到第t+1t+1個循環,只需要維護當前dx,dydx,dy下的前tt層疊加矩陣和前t+1t+1層的疊加矩陣。計算出在前tt個矩陣中不經過11到達當前位置的方案數,和從當前位置開始不經過11,到達當前循環終止位置的方案數,就能愉快的完成轉移了。


代碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define cs const

cs int N=102;
bool ma[N][N];
bool bas[2][N][N];

cs int mod=998244353;
inline int add(cs int &a,cs int &b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(cs int &a,cs int &b){return a<b?a-b+mod:a-b;}
inline int mul(cs int &a,cs int &b){return (ll)a*b%mod;}

int n,m,ans,dx,dy;
int pre[N][N],suf[N][N];

inline void solve(bool b1[N][N],bool b2[N][N],cs int &tx,cs int &ty,int tim){
	for(int re i=1;i<=dx+1;++i)
	for(int re j=1;j<=dy+1;++j)b1[i][j]=b2[i][j]||ma[tx+i][ty+j];
	for(int re i=1;i<=dx+1;++i)
	memset(pre[i]+1,0,sizeof(int)*(dy+1)),
	memset(suf[i]+1,0,sizeof(int)*(dy+1));
	if(b1[1][1]==0){
		pre[1][1]=1;
		for(int re i=1;i<=dx+1;++i)
		for(int re j=1;j<=dy+1;++j)
		pre[i][j+1]=add(pre[i][j+1],(b1[i][j+1]==0)*pre[i][j]),
		pre[i+1][j]=add(pre[i+1][j],(b1[i+1][j]==0)*pre[i][j]);
	}
	if(b2[dx+1][dy+1]==0){
		suf[dx+1][dy+1]=1;
		for(int re i=dx+1;i;--i)
		for(int re j=dy+1;j;--j)
		suf[i][j-1]=add(suf[i][j-1],(b2[i][j-1]==0)*suf[i][j]),
		suf[i-1][j]=add(suf[i-1][j],(b2[i-1][j]==0)*suf[i][j]);
	}
	for(int re i=1;i<=dx+1;++i)
	for(int re j=1;j<=dy+1;++j){
		if(b1[i][j]==0)continue;
		int f;
		if(i==1&&j==1)f=1;
		else f=add(pre[i-1][j],pre[i][j-1]);
		ans=add(ans,mul(mul(tim+i+j-2,f),suf[i][j]));
	}
}

char s[N];
inline void solve(){
	cin>>n>>m;ans=0;
	for(int re i=1;i<=n;++i){
		cin>>(s+1);
		for(int re j=1;j<=m;++j)ma[i][j]=ma[i+n][j]=ma[i+n][j+m]=ma[i][j+m]=s[j]=='1';
	}
	int d=__gcd(n,m);
	for(dx=0,dy=d;dx<=d;++dx,--dy){
		if((dx==0&&n!=1)||(dy==0&&m!=1))continue;
		if(__gcd(dx,d)!=1||__gcd(dx,n)!=1||__gcd(dy,m)!=1)continue;
		for(int re i=0;i<=dx+1;++i)
		memset(bas[0][i]+1,0,sizeof(bool)*(dy+1)),
		memset(bas[1][i]+1,0,sizeof(bool)*(dy+1));
		for(int re tim=0,p=1;tim<n*m;tim+=d,p^=1)
		solve(bas[p],bas[p^1],(tim*dx/d)%n,(tim*dy/d)%m,tim);
	}
	cout<<ans<<"\n";
}

int T;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	cin>>T;
	while(T--)solve();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章