一、题目链接
https://nanti.jisuanke.com/t/31453
二、题意
给定$N$个位置,$2^k$种颜色,让你去涂色,条件是相邻的两种颜色类型异或值的二进制表示不全为$1$(以下简称:异或值不全为$1$),注意,颜色类型的数字是$k$位,不是$32$位,也是$64$位,不要想当然。然后,要注意这$N$个位置是环形的。
三、思路
比较朴素且容易想到的做法:$dp$递推计数。
设$dp[i][0]$表示,在前$i$个位置中,第$i$个位置的颜色和第一个位置的颜色相等的方案数;(这里的“第一个位置”待会儿解释,把题目背景想象成一排就好了);
$dp[i][1]$表示,在前$i$个位置中,第$i$个位置的颜色和第一个位置的颜色异或值为全$1$的方案数;
$dp[i][2]$表示,在前$i$个位置中,第$i$个位置的颜色不是以上两种的其他选取方式(以下简称:取其他的)的方案数;
那么,初始状态:$dp[1][0]=2^k,dp[1][1]=0,dp[1][2]=0$;递推式:
$dp[i][0]=dp[i-1][0]+dp[i-1][2]$,表示:如果第$i$个位置取和第一个位置相等的数字,那么可以由前一个位置取和第一个位置相等的状态,以及由前一个位置取其他数字的方案数转移过来;
$dp[i][1]=dp[i-1][1]+dp[i-1][2]$,表示:如果第$i$个位置取和第一个位置的颜色异或值为全$1$的数字,那么可以由前一个位置取和第一个位置的颜色异或值为全$1$的状态,以及由前一个位置取其他的数字的方案数转移过来;
$dp[i][2]=dp[i-1][0]*(2^k-2)+dp[i-1][1]*(2^k-2)+dp[i-1][2]*(2^k-3)$,表示:如果第$i$个位置取其他的数字,如果前一个位置取的是和第一个位置相等的数字($0$状态),那么,第$i$个位置不能取和第一个位置相等的,也不能取和前一个位置(即和第一个位置相等的那个数字)异或为全$1$的,那么只能取剩余的$2^k-2$种数字。如果前一个位置取的是和第一个位置的颜色异或值为全$1$的数字($1$状态),那么,第$i$个位置不能取和前一个位置的颜色异或值为全$1$的数字,也就是不能取和第一个位置的数字相等的数字,同时也不能取和第一个位置的数字异或为全$1$的,那么只能取剩余的$2^k-2$种数字。如果前一个位置取的是其他数字($2$状态),那么第$i$个位置不能取和第一个位置的数字相等的数字,也不能取和第一个位置的颜色异或值为全$1$的数字,同时也不能取和前一个位置的颜色异或值为全$1$的数字,那么只能取剩余的$2^k-3$种数字。
最后的答案就是$dp[N][0]+dp[N][2]$,即最后一个位置的数字可以取和第一个个位置相等的,也可以取其他的。
注意,这个地方比较绕,如果没看懂,自己稍微理解下,再重新看一遍。
还有就是,为什么环形的可以这样变成一排?
答:其实题目中的环形所能起的作用就是让第$N$个位置和第$1$个位置产生联系,而我们这样已经把每一位和第$1$个位置的联系都搞清楚了,而且在状态转移的过程中把相邻异或为全$1$的非法状态去除掉了,所以答案没错。
另外一点,要注意的是,由前一个位置的状态$1$转移过来的时候,去掉和前一个位置的颜色异或值为全$1$的数字,也就是去掉和第一个位置的数字相等的数字,这儿并没有和前一次$dp[i-1][0]*(2^k-2)$的计算重复,原因是:前一个位置的状态不同,一个是$0$,一个是$1$。
四、代码
#include<bits/stdc++.h> using namespace std; typedef long long LL; #define MAXN 1000010 const LL mod = 1e9 + 7; LL dp[MAXN][3]; LL qpow(LL a, LL x) { LL res = 1; for(; x; x >>= 1) { if(x & 1)res = (res * a) % mod; a = (a * a) % mod; } return res; } int main() { int T, n, k; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &k); LL p2 = qpow(2, k); dp[1][0] = p2; dp[1][1] = dp[1][2] = 0; 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]) % mod) * (p2 - 2 + mod) % mod + dp[i - 1][2] * (p2 - 3) % mod) % mod; } LL ans = (dp[n][0] + dp[n][2]) % mod; printf("%lld\n", ans); } return 0; }