题目链接https://nanti.jisuanke.com/t/31453
题意:n个人围一圈,共有2^k个从0到2^k-1编号的面具,每个人的位置固定,面具有无限个,两两相邻的两个人的面具对应的编号的二进制不能互相为其的补码(构成1111111(共k个)) 求总的方案数
题目分析:这题和环涂色问题非常像,模型是两两之间的颜色不相同。
同理,而且数据规模不大,考虑递推,假设dp[i]为有i个人时的方案数量,需要注意的是这个环的位置是固定的,也就是说我们从n-1个人往其中插入第n个人时只能插入在n-1和1之间。
从dp[n-1]怎么转变为dp[n]呢,有两种转移方向转向dp[n]:
1. 在第n-1位置的人和1位置的人两者的编号相同,这样对第n个人的约束只有一个,n可以选择的编号共有(2^k-1)种,这种情况n-1和1的颜色永远相同,此种情况的方案相当于把这两个人捆绑在一起,也就是dp[n-2],总的合法的方案共有:dp[n-2]*(2^k-1)
2. 在第n-1位置的人和1位置的人两者编号不同,这样对第n个人的约束有两个,可供选择的编号有(2^k-2)种,此种情况下的排列方式共dp[n-1]-dp[n-2]种,但是这样写TLE了。。
这种排列的方式总数是可以求出来的,:对第一个人,无限制条件,共2^k,对第2~n-2个人,有前面的那个人这一个约束,共 (2^k-1)^(n-2) 对最后一个人有前后两个不同的限制条件 共(2^k-2) 由乘法原理共:2^k * (2^k-1) * (2^k-2)
这样,dp[n] = dp[n-2] * (2^k-1) + 2^k * (2^k-1) * (2^k-2) 特判n为奇数偶数时两个边界n=1和n=2
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define ll long long
#define LL ll
const int N = 1000005;
const ll mod=1e9+7;
int n,k;
int t;
ll pow_mod(ll a, ll b, ll p)
{
ll ret = 1;
while(b)
{
if(b & 1) ret = (ret * a) % p;
a = (a * a) % p;
b >>= 1;
}
return ret;
}
ll solve(int n,int k)
{
ll f=pow_mod(2,k,mod);//2^k
ll ans=0;
if(n==1)
{
return f;
}
else if(n==2)
{
return (f*(f-1))%mod;
}
else
{
return (f*pow_mod(f-1,n-2,mod)%mod*(f-2)%mod+solve(n-2,k)%mod)%mod;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
cin >> n >> k;
cout<<solve(n,k)<<endl;
}
return 0;
}