码迷,mamicode.com
首页 > 其他好文 > 详细

快速幂+地推

时间:2018-09-12 23:57:11      阅读:216      评论:0      收藏:0      [点我收藏+]

标签:第一个   之间   res   个人   targe   题意   names   快速   需要   

题目传送门

题意:有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;
    }
}

 

快速幂+地推

标签:第一个   之间   res   个人   targe   题意   names   快速   需要   

原文地址:https://www.cnblogs.com/linhaitai/p/9638023.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!