ACM-ICPC 2018 徐州赛区网络预赛-A.Hard to prepare-组合数学+递归

这里写图片描述
这里写图片描述

题意:

有n个点,每个点可从[0~2^k-1]中任取一值,n个点围成一个圆,要求相邻两点值的nxor(同或)值不为0,求方案数

思路:

根据题意可推出,对于一个值X,只有唯一一个值~X不能与其相邻
对于第一个点,有2^k种选择
对于第2~n-1个点,每个位置有2^k-1种选择,共有(2^k-1)^n-2种选择
对于第n个点,因其不能与第n-1个点和第1个点nxor为0,所以有2^k-2种选择
由上述三点可推出,N=2^k*[(2^k-1)^(n-2)]*(2^k-1)

由于每个点的值是任取的,所以还可能存在第1个点的值与第n-1个点的值相同的情况,此时第n个点只有2^k-1种选择
对于这种情况的状态数,就相当于在一个由n-2个点构成的环上,将第1个点拆分成两个相同的点——第1点和第n-1点,然后在第n-1点后添加第n点
所以此时的情况数为F(n-2,k)

结合上述两种情况,最终得出方案数:F(n,k)=2^k*[(2^k-1)^(n-2)]*(2^k-1)+F(n-2,k)

其它方法:

由于使用递归自顶向下求解,显然可以改为自底向上的动态规划做法,但递归做法更符合组合数学的推理过程,更容易想到

代码:

#include <iostream>
using namespace std;

typedef long long LL;
const long long mod=1e9+7;
LL pow(LL a,LL b)        //快速幂
{
    LL ans=1;
    while(b)
    {
        if(b%2)
            ans=ans*a%mod;
        a=a*a%mod;
        b/=2;
    }
    return ans;
}
LL p[1000005]={1};       //存储2^k
LL F(LL n,LL k)
{
    LL ans;
    if(n==2)
        return p[k]*(p[k]-1)%mod;
    if(n==1)
        return p[k];
    ans=(p[k]*pow((p[k]-1),n-2)%mod*max(p[k]-2,0LL)%mod+F(n-2,k))%mod;
    return ans;
}
int main()
{
    int t;
    for(int i=1;i<1e6+5;i++)
        p[i]=p[i-1]*2%mod;
    cin>>t;
    while(t--)
    {
        int n,k;
        cin>>n>>k;
        cout<<F(n,k)<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43093481/article/details/82627496