题目链接:传送门

题意就不说了

思路:

一开始比赛的时候就是想   

k = 2^m

答案等于 k*((k-1)^(m-1)) 发现多了情况 因为是个环

然后换成 k*((k-2)^(m-1))*(k-2) 发现漏算了第一个和倒数第二个相同的情况

看了网上的其他题解,包括比赛时打的表明白了这其中的规律

那么我们可以先算出第一个和倒数第二个不同的情况,而当他们相同的时候那么最后一个就有(k-1)种情况,那么就相当于整个长度被减去了2,得出的结果再加上去,可以由此进行dfs,因为给的n的值不大,时间复杂度是O(n),最后得出结果。

#include<bits/stdc++.h>
#define ll long long
#define MOD 1000000007
#define MAXN 1000005
using namespace std;
ll inv[MAXN];
ll k, n, m;
void init() {
	inv[1] = 2;
	for(int i = 2; i < MAXN; i++){
		inv[i] = inv[i-1] * 2LL % MOD;
	}
}
ll qpow(ll a, ll b){
	ll t = a, ans = 1;
	while(b) {
		if(b % 2){
			ans = ans * t % MOD;
		}
		t = t * t % MOD;
		b /= 2;
	}
	return ans;
}
ll cheak(ll len){
	if(len == 1){
		return k;
	}
	if(len == 2){
		return k * (k - 1) % MOD;
	}
	ll res = 0;
	res = k * qpow(k - 1, len - 2) % MOD * (k - 2) % MOD + cheak(len - 2) % MOD;
	return res;
}

int main() {
	int t;
	scanf("%d", &t);
	init();
	while(t--) {
		scanf("%lld%lld", &n, &m);
		k = inv[m] % MOD;
		printf("%lld\n", cheak(n) % MOD);
	}
	return 0;
} 

再搞上一波在别的博客学到的dp做法:

定义:

dp[i][0] 至第i个人分发面具的方案数,且 mask[i] = mask[1]

dp[i][1]至第i个人分发面具的方案数,且 mask[i] XNOR mask[1] = 0

dp[i][2]至第i个人分发面具的方案数,且 mask[i] ≠ mask[1],mask[i] XNOR mask[1] ≠ 0

AC_code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 1000005
#define MOD 1000000007
ll inv[MAXN];
ll dp[MAXN][3];
void init() {
	inv[0] = 1;
	for(int i = 1; i < MAXN; i++){
		inv[i] = inv[i-1] * 2LL % MOD;
	}
	return ;
}
ll DP(int n, int m) {
	dp[1][0] = 1;
	for(int i = 2; i <= n; i++){
		dp[i][0] = (dp[i - 1][0] + dp[i - 1][2]) % MOD;
		dp[i][1] = (dp[i - 1][1] + dp[i - 1][2]) % MOD;
		dp[i][2] = (dp[i - 1][0] + dp[i - 1][1]) * (inv[m] - 2 + MOD) % MOD + dp[i - 1][2] * (inv[m] - 3 + MOD) % MOD;
	}
	return (dp[n][0] + dp[n][2]) * inv[m] % MOD;
}
int main() {
	int n, m, t;
	scanf("%d", &t);
	init();
	while(t--) {
		scanf("%d%d", &n, &m);
		ll ans = DP(n, m);
		printf("%lld\n", ans);
	}
	return 0;
}