题目传送门
这道题,乍一看很难,但其实就是一道推结论的数学题。
相信大家在小学的时候都做过这样一道数学题吧:大意就是如何用最少的正整数凑出最多的连续的正整数(随便口胡了一下,差不多就行了),其思路大致如下:
- 先取1,因为这是最小的正整数。
 
- 再取2,因为一个1凑不出2。
 
- 再取4,因为1+2=3,无法凑出4。
 
- 再取8,因为1+4=5,2+4=6,1+2+4=7,凑不出8。
 
- ⋯
 
由此,我们可以看出一个规律,当每次取前面取过的所有数的和再加一时,可以凑出最多的正整数,即:
mi=j=1∑i−1mj+1
铺垫完毕。
 过程
接下来我们进入这道题的分析。
根据上述规律,我们可以看出,这道题其实也有着异曲同工之妙,由于有m个数可以取任意值,我们就按照以上策略来取(由于n代表的是有n个1,所以可以当成是补漏的),设xi表示第i次取的值,则有:
x1=n+1
x2=x1+n+1=2⋅n+2=2⋅(n+1)
x3=x1+x2+n+1=4⋅n+4=4⋅(n+1)
x4=x1+x2+x3+n+1=8⋅n+8=8⋅(n+1)
⋯
xm=2m−1⋅(n+1)
此时,我们能表示出来的从1开始的连续的正整数就有这么多:
i=1∑mxi+n
即:
(1+2+4+8+⋯+2m−1)⋅(n+1)+n
怎么样,前面那个式子是不是似曾相识?没错,这是我们小学所学的等比数列!只要把最后的n变成n+1−1,并把n+1合并到前面的那一坨东西里面,就可以弄出这个式子:
2m⋅(n+1)−1
具体过程我不详细解释了,比较简单,手推一下就行了。既然都弄出了这个式子,那我们就可以偷税地去算了。
对了,由于m≤109,直接暴力算乘方明显会超时,所以我们需要用快速幂(不会的同学建议去做一下 这道题)。
 参考代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
   | #include<bits/stdc++.h> #define ll long long #define p 1000000007 using namespace std; ll n,m,t; inline ll input() {     ll x=0,f=1;     char c=getchar();     while(c<'0'||c>'9') 	{         if(c=='-') 			f=-1;         c=getchar();     }     while(c>='0'&&c<='9') 	{         x=x*10+c-'0';         c=getchar();     }     return x*f; } inline ll ksm(ll b,ll k) { 	ll ans=1; 	while(k) 	{ 		if(k&1) 			ans=ans*b%p; 		b=b*b%p; 		k>>=1; 	} 	return ans%p; } int main() {  	n=input();  	m=input();  	printf("%lld",ksm(2,m)*(n+1)%p-1); 	return 0; }
   | 
 
 后记
这道题其实不算很难吧,主要就是推式子那里麻烦了一点,然后要用到快速幂,其他的也并不是很麻烦,不想用读入优化也是可以的,只是因为比赛的时候想优化一下(其实我还加了八聚氧只不过太长了所以就删掉了)。