P4621 [COCI2012-2013#6] BAKTERIJE 题解
一道很好的数学题。
首先不难想到每个细菌的移动路线是有循环节的,循环节外的时间最多就是每个格子的四个方向都走一遍,也就是 \(4\times N \times M\)。
可以预处理每个细菌分别通过四个方向第一次到达终点的时间 \(b_{i,0/1/2/3}\) 和再次回到当前状态的循环节长度 \(md_{i,0/1/2/3}\)。
要特别注意的是不同方向到达终点是不同的状态。
首先 \(4\times N \times M\) 以内的答案直接暴力算出来(有可能有病毒没有进入循环节)。
然后计算大于 \(4\times N \times M\) 的答案,假设已知了每个病毒最终到达终点面对的方向 \(face_i\),其实限制就是一个同余方程,设答案为 \(x\),对于每个病毒有限制:
\[x\equiv b_{i,face_i}(\bmod md_{i,face_i})
\]
可以扩展中国剩余定理求解,每个病毒面对的方向可以搜索。
有一个细节,扩展中国剩余定理求解的答案必须大于等于所有的 \(b_{i,face_i}\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=60;
const int INF=1e18;
int as=INF;
int n,m,k,rx,ry;
int dx[10]={-1,0,1,0},dy[10]={0,1,0,-1};
int tun[10]={2,3,0,1};
int vis[N][N][4],b[N][4],md[N][4],rec[N][N];
int mp[N][N],x,y;
int ton[2*N*N*4];
int hp[4],cho[N],lim=0;
struct node{
    int x,y;
    char c;
}q[N];
bool out(int xx,int yy){
    if(xx>n || xx<1 || yy>m || yy<1) return true;
    return false;
} 
int go(int xx,int yy,int st){
    st+=mp[xx][yy];
    st%=4;
    if(!out(xx+dx[st],yy+dy[st])) return st;
    else return tun[st];
}
void work(int now,int nx,int ny,char ch){
    int st;
    if(ch=='U') st=0;
    else if(ch=='R') st=1;
    else if(ch=='D') st=2;
    else st=3;
    int tim=0;
    hp[0]=hp[1]=hp[2]=hp[3]=0;
    int xf=INF;
    while(tim<=n*m*4){
        ++tim;
        if(nx==rx && ny==ry){
            xf=min(xf,tim);
            if(b[now][st]==INF) b[now][st]=tim;
            else if(md[now][st]==INF) md[now][st]=tim-b[now][st];
            ton[tim]++;
            hp[st]=tim;
        }
        st=go(nx,ny,st);
        nx+=dx[st],ny+=dy[st];
    }
    lim=max(lim,xf);
}
void init(){
    cin>>n>>m>>k;
    cin>>rx>>ry;
    for(int i=0;i<N;i++){
        for(int j=0;j<4;j++) b[i][j]=md[i][j]=INF;
    }
    for(int i=1;i<=k;i++){
        cin>>q[i].x>>q[i].y>>q[i].c;
        string s;
        for(int j=1;j<=n;j++){
            cin>>s,s=" "+s;
            for(int k=1;k<=m;k++) mp[j][k]=(s[k]-'0');
        }
        work(i,q[i].x,q[i].y,q[i].c);
    }
}
int exgcd(int a,int b){
	if(b==0){
		x=1,y=0;
		return a;
	}
	else{
		int as=exgcd(b,a%b);
		int xx=x,yy=y;
		x=yy,y=xx-(a/b)*yy;
		return as;
	}
}
int qmul(int af,int bf,int p){
	int aa=0,x=af;
	while(bf){
		if(bf&1){
			aa+=x;
			aa%=p;
		}
		x*=2;
		x%=p;
		bf/=2;
	}
	return aa;
}
int excrt(){
    int ans=b[1][cho[1]],lcm=md[1][cho[1]];
	for(int i=2;i<=k;i++){
        int aa=lcm,bb=md[i][cho[i]],cc=b[i][cho[i]]-ans;
		cc=(cc%md[i][cho[i]]+md[i][cho[i]])%md[i][cho[i]];
		int d=exgcd(aa,bb);
		if(cc%d){
		    return INF;
		}
		ans=ans+lcm*qmul(x,cc/d,md[i][cho[i]]);
		lcm/=d,lcm*=md[i][cho[i]];
		ans=(ans%lcm+lcm)%lcm;
	}
    int af=ans;
    if(lim>af){
        int xx=(lim-af)/lcm;
        af+=(xx*lcm);
        if(af<lim) af+=lcm; 
    }
    return af;
}
void dfs(int x){
    if(x==k+1){
        as=min(as,excrt());
        return;
    }
    for(int i=0;i<4;i++){
        if(b[x][i]==INF || md[x][i]==INF) continue;
        cho[x]=i;
        dfs(x+1);
    }
}
signed main(){
    init();
    for(int i=1;i<=n*m*4;i++){
        if(ton[i]==k){
            cout<<i;
            return 0;
        }
    }
    dfs(1);
    if(as==INF) cout<<-1;
    else cout<<as;
    return 0;
}
    在那高远的黑色穹顶之下,它的牺牲使圣巢永世不衰

                
            
        
浙公网安备 33010602011771号