ACM-ICPC 2018 徐州赛区网络预赛 A.Hard to prepare(dp)

题目

n(n<=1e6)个人围成一个环,每个人可以从0到2^{k}-1(k<=1e6)这些数里选一个数,每个数可以被重复选

但当前人i不能和他相邻的人j选的数的同或值为0,求满足题意的方案数%(1e9+7)

同或值为0,即所有对应位置二进制位值都相反,如4和3同或值就为0

思路来源

https://blog.csdn.net/Originum/article/details/82598329

题解

记dp[i][0]为长度为i的首尾值相同的方案数(形如x ... x)

dp[i][1]为长度为i的首尾值同或值为0(不妨称为相反)的方案数(形如 x ... x非)

dp[i][2]为长度为i的首尾既不相同也不相反的方案数(形如x ... y,其中y不等于x,y不等于x非)

x ... x 可以后缀x 形成dp[i+1][0],x ... y可以后缀x 形成dp[i+1][0];

x ... x非 可以后缀 x非 形成dp[i+1][1],x ... y可以后缀 x非形成 dp[i+1][1];

x ... x 可以后缀y(y有m-2种取法,除去x、x非) 形成dp[i+1][2];

x ... x非 可以后缀 y(y有m-2种取法,除去x、x非)形成dp[i+1][2];

x ... y 可以后缀 y'(y'有m-3种取法,除去x、x非、y非)形成dp[i+1][2];

dp[i][0]=(dp[i-1][0]+dp[i-1][2])%mod;
dp[i][1]=(dp[i-1][1]+dp[i-1][2])%mod;
dp[i][2]=(dp[i-1][0]*(m-2)%mod+dp[i-1][1]*(m-2)%mod+dp[i-1][2]*(m-3)%mod)%mod;

乘法部分注意一下,不要爆ll,取模的地方尽量写规范

心得

递推式dp主要定义状态比较难,和数位dp给人的感觉是一致的

又复习了一下快速乘吧,

快速乘,用减法代替取模,用加法代替乘法,其余和快速幂基本一致

dp[2][1]=0看似很迷,毕竟画图的话dp[2][1]应该是等于m的

实则用dp[i][1]=dp[i-1][1]+dp[i-1][2]可以解释,所有项都该是符合递推式的鸭

就这样吧,无需深究

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e6+10;
int t;
ll n,k;
ll m,dp[maxn][3];
ll modmul(ll x,ll n,ll mod)
{
	x%=mod;n%=mod;
	ll res=0;
	for(;n;n/=2)
	{
		if(n&1)
		{
		 res+=x;
		 if(res>=mod)res-=mod;
	    }
		x+=x;
		if(x>=mod)x-=mod;
	}
	return res;
}
ll modpow(ll x,ll n,ll mod)
{
	ll res=1;
	for(;n;n/=2)
	{
		if(n&1)res=modmul(res,x,mod);
		x=modmul(x,x,mod);
	}
	return res;
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld%lld",&n,&k);
		m=modpow(2,k,mod);
		dp[1][0]=m;dp[1][1]=0;dp[1][2]=0;
		dp[2][0]=m;dp[2][1]=0;dp[2][2]=modmul(m,m-2,mod);
		for(int i=3;i<=n;++i)
		{
			dp[i][0]=(dp[i-1][0]+dp[i-1][2])%mod;
			dp[i][1]=(dp[i-1][1]+dp[i-1][2])%mod;
			dp[i][2]=(dp[i-1][0]*(m-2)%mod+dp[i-1][1]*(m-2)%mod+dp[i-1][2]*(m-3)%mod)%mod;
		}
		printf("%lld\n",(dp[n][0]+dp[n][2])%mod);
	}
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值