【NOIP2018提高组D2T2】填数游戏

此题较为特殊,有两种打法
1.打表过
2.正解

打表的:

#include<bits/stdc++.h>
using namespace std;
#define mo 1000000007ll
typedef long long ll;
ll n,m,ans1[20],ans2[20];
ll qp(ll x,ll y){
	if(!y)return 1;
	if(y==1)return x%mo;
	ll r=qp(x,y/2);
	if(y%2==1)return r*r%mo*x%mo;
	return r*r%mo;
}
//r[n][n]=r[n-1][n-1]*8-5*2^n
//r[n][n+1]=r[n][n]*3-3*2^n
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	if(n>m)swap(n,m);
	if(n==1) printf("%lld\n",qp(2,m));
	else if(n==2) printf("%lld\n",4*qp(3,m-1)%mo);
	else if(n==3) printf("%lld\n",112*qp(3,m-3)%mo);
	else
	{
		ans1[4]=912;
		for(ll i=5;i<=10;i++)
			ans1[i]=(ans1[i-1]*8ll-5ll*qp(2,i)%mo+mo)%mo;
		for(ll i=4;i<=10;i++)
			ans2[i]=(ans1[i]*3-3*qp(2,i)%mo+mo)%mo;
		if(n==m) printf("%lld\n",ans1[n]);
		else printf("%lld\n",ans2[n]*qp(3,m-n-1)%mo);
	}
}

正解:

#include <cstdio>

#define ll long long
#define F(i, a, b) for (ll i = a; i <= b; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define mx(a, b) ((a) = max(a, b))
#define add(a, b) ((a) = (a + b) % Mo)

const ll Mo = 1e9 + 7, N = 1e6 + 10, M = 100;
using namespace std;

ll n, m, ans, ANS1, ANS2, a1, a2, f[N], S[M];
bool bz, bz1, bz2, bz3; ll all;

ll ksm(ll x, ll y) {
	ll ans = 1;
	for (; y; y >>= 1, x = (1LL * x * x) % Mo)
		if (y & 1)
			ans = (1LL * ans * x) % Mo;
	return ans;
}

void Go(ll now, ll x, ll y) {
	if (bz) return;
	if (x == n && y == m) {
		if (now < all) { bz = 1; return; }
		mx(all, now); return;
	}
	if (y < m) Go(now * 10 + S[(x - 1) * m + y], x, y + 1);
	if (x < n) Go(now * 10 + S[(x - 1) * m + y], x + 1, y);
}

void Dfs(ll k) {
	if (k - 1 > m && (k - 1) % m && S[k - 1] < S[k - m]) return;
	if (k - 1 > m && S[m + 1] == S[2]) bz1 = 1; else bz1 = 0;
	if (k - 1 > m + 1 && S[m + 2] == S[3]) bz2 = 1; else bz2 = 0;
	if (k - 1 > 2 * m && S[2 * m + 1] == S[m + 2]) bz3 = 1; else bz3 = 0;
	if (bz1 && (k - 1) % m > 1 && k - 1 > 2 * m && S[k - 1] != S[k - m]) return;
	if (bz2 && (k - 1) % m > 2 && k - 1 > 2 * m && S[k - 1] != S[k - m]) return;
	if (bz3 && (k - 1) % m > 1 && k - 1 > 3 * m && S[k - 1] != S[k - m]) return;
	if (k > n * m) {
		if (S[1] || S[n * m]) return;
		bz = all = 0, Go(0, 1, 1), ans += !bz;
		return;
	}
	S[k] = 1, Dfs(k + 1);
	S[k] = 0, Dfs(k + 1);
}

int main() {
	freopen("game.in", "r", stdin);
	freopen("game.out", "w", stdout);
	scanf("%d%d", &n, &m);
	if (n > m) n += m, m = n - m, n = n - m;
	if (n == 1 || n == 2) {
		printf("%d\n", n == 1 ? ksm(2, m) : 12 * ksm(3, m - n) % Mo);
		return 0;
	}
	if (n == 3) {
		printf("%d\n", 112 * ksm(3, m - n) % Mo);
		return 0;
	}
	if (n * m <= 20) {
		Dfs(1);
		printf("%d\n", ans * 4);
		return 0;
	}
	F(i, 1, n - 4)
		f[i + 1] = (4 * 5 + f[i] * 4) % Mo; // 4表示上一条对角线第一次成对儿.
	ans = 2 * (3 + 4 * 3 + f[n - 3] * 2) * ksm(2, n - 1);
	a1 = ksm(4, n - 2) * ksm(2, n + 1) % Mo;
	a2 = 5 * ksm(4, n - 4) * ksm(2, n + 1) % Mo;
	if (n == m) { printf("%d", (ans + a1 + a2) % Mo); return 0; }
	ANS1 = (4 + 4 * 4 + (f[n - 3] * 3 + 4 * 3) * 2) * ksm(2, n - 1) % Mo;
	ANS2 = (f[n - 3] * 3 + 4 * 5) * ksm(2, n) % Mo;
	printf("%d", ((((a1 + a2) * 3 + ANS1 + ANS2) % Mo) * ksm(3, m - n - 1)) % Mo);
}
posted @ 2018-12-15 16:39  jz929  阅读(290)  评论(0编辑  收藏  举报