COCI2016/2017 Contest#4 F

Description:

给出一个 n ⋅ m n \cdot m nm的字符矩阵,将此矩阵无限延长,现在选一个起点和一个方向(8个方向)走 K K K步得到的字符串,求走两次得到的两个字符串相同的概率。
n , m ≤ 200 , K ≤ 1 0 9 n,m\le 200,K\le 10^9 n,m200,K109

Solution:

  • 对于这道题的出发点,个人觉得还是这个 K K K的范围太敏感了。
  • 而且题意刻画的还是经典的走 K K K步的问题,那么利用倍增的思想,将 K K K二进制处理。
  • 又因为路径的方向是固定的,那么我们可以枚举方向,一个一个解决。
  • 这里,对于路径的记录,我们通常是用 H a s h Hash Hash,这里也不例外。
  • 接着,我们可以定义倍增 d p [ k ] [ i ] [ j ] dp[k][i][j] dp[k][i][j]表示 ( i , j ) (i,j) (i,j) 2 k − 1 2^k-1 2k1步到的情况,故,这里的 d p dp dp中需要记录下个点以及路径的 H a s h Hash Hash值。
  • 对于转移,这里因为是结构体包着,不好表达,建议看代码注释…
  • 最后,需要统计的答案,我们只好将所有终态的记录,最后排序,计算相同的贡献即可。

Code:

#include
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i=i##_end_;--i)
#define ll long long
#define ull unsigned long long
#define fi first
#define se second

const int N=202,M=21;
const int d[]={0,-1,1};

ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}

int n,m,K;
char s[N][N];

typedef pairpii;

struct node{
	int x,y;
	pii v;
}dp[M][N][N];
/*
	dp[k][i][j] 表示 (i,j) 走 2^k-1步的情况 
*/ 
int bit[M],num;
int tot;
pii A[N*N*8];
pii Step[M],Base[M];
ll base1=133,base2=137;
/*
	这里Hash还是建议双Hash,懒一点可以用unsigned long long,一般卡不掉...
	强烈建议多Hash时将Hash包个struct,再写一个重载符,个人觉得比函数较方便
*/ 
pii operator*(pii a,pii b){
	pii c;
	c.fi=a.fi*b.fi;
	c.se=a.se*b.se;
	return c;
}

pii operator+(pii a,pii b){
	pii c;
	c.fi=a.fi+b.fi;
	c.se=a.se+b.se;
	return c;
} 

node DP(node a,pii BASE,int nxt){
	node b=dp[nxt][a.x][a.y];
	return (node){b.x,b.y,b.v*BASE+a.v};
				// 下个点	//路径Hash 
}

void solve(int Dx,int Dy){
	SREP(x,0,n) SREP(y,0,m){
		int nx=(x+Dx+n)%n;
		int ny=(y+Dy+m)%m;
		dp[0][x][y]=(node){nx,ny,make_pair(s[x][y],s[x][y])};//初始 
	}
	
	REP(i,1,bit[num-1]) SREP(x,0,n) SREP(y,0,m) dp[i][x][y]=DP(dp[i-1][x][y],Base[i-1],i-1);
	
	SREP(x,0,n) SREP(y,0,m) {
		node res=(node){x,y,(pii){0,0}};
		SREP(i,0,num) res=DP(res,Step[i],bit[i]);
		A[++tot]=res.v;// 记录该方向上(x,y)的终态 
	}
}

int main(){
//	freopen("genius.in","r",stdin);
//	freopen("genius.out","w",stdout);
	scanf("%d%d%d",&n,&m,&K);
	SREP(i,0,n) scanf("%s",s[i]);
	
	Base[0]=(pii){(ull)base1,(ull)base2};
	SREP(i,1,M) Base[i]=Base[i-1]*Base[i-1];
	SREP(i,0,M) if(K&(1<

你可能感兴趣的:(COCI)