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;
        }
    }
  • 相关阅读:
    js中undefined,null,NaN的区别
    js中数字计算精度
    BestCoder Round #32
    POJ 2299 求逆序对(归并排序或树状数组)
    POJ 2603
    CodeForces 515C
    POJ 1853 背包问题
    UVA 10115 子符串替换
    POJ 1155 树状dp
    HDU 2196 树状dp 求树中节点之间的最长距离
  • 原文地址:https://www.cnblogs.com/linhaitai/p/9638023.html
Copyright © 2011-2022 走看看