2018 icpc 徐州站网络赛 A hard to prepar (组合数学 递推)

题目链接https://nanti.jisuanke.com/t/31453

题意:n个人围一圈,共有2^k个从0到2^k-1编号的面具,每个人的位置固定,面具有无限个,两两相邻的两个人的面具对应的编号的二进制不能互相为其的补码(构成1111111(共k个)) 求总的方案数

题目分析:这题和环涂色问题非常像,模型是两两之间的颜色不相同。

同理,而且数据规模不大,考虑递推,假设dp[i]为有i个人时的方案数量,需要注意的是这个环的位置是固定的,也就是说我们从n-1个人往其中插入第n个人时只能插入在n-1和1之间。

从dp[n-1]怎么转变为dp[n]呢,有两种转移方向转向dp[n]:

1. 在第n-1位置的人和1位置的人两者的编号相同,这样对第n个人的约束只有一个,n可以选择的编号共有(2^k-1)种,这种情况n-1和1的颜色永远相同,此种情况的方案相当于把这两个人捆绑在一起,也就是dp[n-2],总的合法的方案共有:dp[n-2]*(2^k-1)

2. 在第n-1位置的人和1位置的人两者编号不同,这样对第n个人的约束有两个,可供选择的编号有(2^k-2)种,此种情况下的排列方式共dp[n-1]-dp[n-2]种,但是这样写TLE了。。

这种排列的方式总数是可以求出来的,:对第一个人,无限制条件,共2^k,对第2~n-2个人,有前面的那个人这一个约束,共  (2^k-1)^(n-2) 对最后一个人有前后两个不同的限制条件 共(2^k-2)   由乘法原理共:2^k * (2^k-1) * (2^k-2)

这样,dp[n] = dp[n-2] * (2^k-1)  +  2^k * (2^k-1) * (2^k-2)  特判n为奇数偶数时两个边界n=1和n=2

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define ll long long
#define LL ll
const int N = 1000005;
const ll mod=1e9+7;
int n,k;
int t;
ll pow_mod(ll a, ll b, ll p)
{
    ll ret = 1;
    while(b)
    {
        if(b & 1) ret = (ret * a) % p;
        a = (a * a) % p;
        b >>= 1;
    }
    return ret;
}


ll solve(int n,int k)
{
    ll f=pow_mod(2,k,mod);//2^k
    ll ans=0;
    if(n==1)
    {
        return f;
    }
    else if(n==2)
    {
        return (f*(f-1))%mod;
    }
    else
    {
        return (f*pow_mod(f-1,n-2,mod)%mod*(f-2)%mod+solve(n-2,k)%mod)%mod;
    }
}


int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin >> n >> k;
        cout<<solve(n,k)<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neuq_zsmj/article/details/82564358
今日推荐