题意:有n个人坐成一圈,每个人都戴着一个面具,面具有0-2^k-1种编号,每相邻的两个人的面具的编号的二进制表示中必须存在至少一位相同,问总共有多少种排列方法。
思路:
我们可以把这个圈从某一处裁开,使之变成一条线,令长度为n的直线上编号两两之间有相同的二进制位的排列方法为line(n),编号两两之间有相同的二进制位但是首位的二进制表示完全不同,记为linedf(n)。则当圆的周长为n时的答案就是
line(n)-linedf(n)
再来推导line(n),由于只需要与前一个人的面具的编号不是完全不相同就可以了。因此
line(n)=line(n-1)*(2^k-1)
再来推导linedf(n),由于linedf(n)指的是第n个人和第一个人完全不相同的情况,因此,第n-1位不能与第一位相同,那么linedf(n)=line(n-1)-第n-1位与第一位完全相同的情况数。不难发现,第一位与x位完全相同就能使1到x-1连成一个环,因此第n-1位与第一位完全相同的情况数就是line(n-2)-linedf(n-2)。所以:
linedf(n)=line(n-1)-(line(n-2)-linedf(n-2));
#include<iostream> #include<algorithm> #include<math.h> using namespace std; typedef long long LL; const LL mod=1e9+7; const LL MAX_E=1e6+5; LL p; LL dp[MAX_E],fals[MAX_E]; LL mod_pow(LL x,LL n) { LL res=1; while(n>0) { if(n&1) res=res*x%mod; x=x*x%mod; n >>= 1; } return res; } LL solve(LL n,LL k) { for(int i=2;i<=n;i++) { dp[i]=dp[i-1]*(mod_pow(2,k)-1)%mod; fals[i]=(dp[i-1]-(dp[i-2]-fals[i-2]))%mod; } return (dp[n]+mod-fals[n])%mod; } int main() { int T; cin>>T; while(T--) { LL n,k; cin>>n>>k; p=mod_pow(2,k); dp[0]=dp[1]=p; cout<<solve(n,k)<<endl; } }