zoukankan      html  css  js  c++  java
  • 快速幂+地推

    题目传送门

    题意:有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;
        }
    }
  • 相关阅读:
    .net MVC 下载文件乱码问题解决方案
    javascript将json转字符串
    js中将字符串转换成json的三种方式
    mvc项目,导出到Excel,中文显示乱码
    20160606面试题总结
    bzoj 4318: OSU!
    bzoj 1419: Red is good
    Codeforces 123 E Maze
    HDU 4336 Card Collector
    Codeforces 540 D Bad Luck Island
  • 原文地址:https://www.cnblogs.com/linhaitai/p/9638023.html
Copyright © 2011-2022 走看看