CCF2013-13-4——有趣的数

写在前面

做这道题时发现我现在的计算能力有点差,算错了好几次(导致我多做了几个小时),这里严重自我批评一下!!!做这道题给我的感觉是在做数学题而不是ccf,很奇怪,后来在网站查了一下,如果用算法的思维去思考,确实简单很多。

题目

在这里插入图片描述

我的分析(以下涉及计算)

首先,读完题,有几个要点得知道:
1.首位一定是2;
2.0和2不能在末尾;
3.左边第二位不能为1;
鉴于第二位的情况只有三个,我们进行分类讨论

分类1

左边第二位是2,那么这种情况下的答案为n-1位的答案(这种情况相当于在n-1位基础上,在最高位前面加个2)

分类2

左边第二位0,现在我们来思考一下,假设这个数中,为1的最高位为x,为3的最高位为y,假设x>y(也就是1的最高位在3的最高位的前面),例如201133113,则x为7(第七位为1,往后没有1了)y为5(第五位为3,往后没有3了),现在我们把数分成三段:
第一段,左边第二位(0)到x(为1的最高位)之间:
这之间的数只可能是0或2,不可能是1或3,那么这之间有n-2-x-1(这里多减的1是除开第x位的1)个数,每个位置两种情况,一共有2(n-2-x-1)种;
第二段,x到y之间:
这之间的数只可能是1或2,不可能是0或3,那么这之间一共有x-y-1个数,每个位置两种情况,一共有2(x-y-1)种;
第三段,y-第一位之间
这之间的数值可能是1或3,不可能是0或2,那么这之间一共有y-1个数,每个位置两种情况,一共有2(y-1)种;
也就是说对于每个确定的x和y,有2(n-4)种情况,那么这样的x和y有多少种呢?可用排列组合求出,也就是n-2个位置里选出2个,就是C2n-2=(n-2)(n-3)/2,但是别忘了,这是x>y时,还有y>x时(分析不变),所以:
分类2一共有(n-2)(n-3)2(n-4)种情况

分类3

左边第二位是3,我们可以像5一样考虑,不过这次假设为0的为高位为x,为1的最高位为y,且x>y和x>1(这个是必须的,因为0必须在1前面,所以x不能为1,还要大于y),重复分类2的操作,分为三段,这里直接说结论,第一段只能为3,第二段只能为0或3,但三段只能为1或3,所以
分类3一种有(n-4)2(n-3)+1种情况(公式可以自己推,但是要注意这里的x不能为1,而在分类2中x可以为1)
答案就是三种情况相加即可

数据处理

但是值得注意的是,如果n很大,假设为80,那么2(80-4)就很大了,超过了64位,所以我们要进行降幂处理,这里可以降为30或29,我降的30,因为230>1000000007>229,那么2(n-4)=(230)(n-4)/30*2(n-4)%30;假设m=230,y=(n-4)/30,x=(n-4)%30,那么就是变为my2x,m可以写成m-1000000007+1000000007;然后对(m-1000000007+1000000007)y,进行二项式展开(注意把m-1000000007看成一项)于是你就会发现,对1000000007求余的结果就是(m-1000000007)y对1000000007求余的结果(这在求余中经常用到)也就是:
my%1000000007=(m-1000000007)y%1000000007;
这样处理后就可以接受了(当然你可以不减去1000000007),因为m的平方对于64位数来说绰绰有余,这里主要是提一下一个求余的方法;
于是代码如下(注意每次结果求余,避免数据溢出)

#include <iostream> 
#include <math.h>
const int tmp=1000000007;
const int h=pow(2,30)-tmp;
using namespace std;
int find(int n)
{
	if(n==4)
	return 3;
	else
	{
		int i,m,y;
		long long int x,ans=0;
		
		//第二位为3 
		m=(n-3)/30;y=(n-3)%30;
		x=1;
		if(n>32)
		x=h;
		for(i=1;i<m;i++)
		{
			x=(x*h)%tmp;
		}
		x=x*pow(2,y);
		x=x%tmp;
		x=x*(n-4)+1;
		x=x%tmp;
		ans=x;
		
		//第二位为0 
		m=(n-4)/30;y=(n-4)%30;
		x=1;
		if(n>33)
		x=h;
		for(i=1;i<m;i++)
		{
			x=(x*h)%tmp;
		} 
		x=x*pow(2,y);x=x%tmp;
		x=x*(n-3)*(n-2)%tmp;
		ans+=x;
		
		//第二位为2 
		ans+=find(n-1);
		ans=ans%tmp;
		return ans;
	}
}
int main()
{
	int n,ans;
	cin>>n;
	ans=find(n);
	
	cout<<ans<<endl;
}

结语

看了看网上的思路,思路挺清晰简单的,数位dp,可自行查阅,值得一提的是,我个人觉得,看了数位dp感觉自己白学了算法,这种思路和我原来的思路就好像是一个计算机系的人和一个非计算机系做的一样,给我的感觉就是,数位dp不那么在乎数字是啥,只在乎含有那些数字,至于数字的顺序怎样,不用去了解(觉得这种想法有点天马行空但有有道理),就好像第一次学递归的汉诺塔一样,写出怎么做,至于具体咋做的由计算机处理。(越细想越感觉这种思想好变态啊!!!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值