题目传送门
这道题,乍一看很难,但其实就是一道推结论的数学题。
相信大家在小学的时候都做过这样一道数学题吧:大意就是如何用最少的正整数凑出最多的连续的正整数(随便口胡了一下,差不多就行了),其思路大致如下:
- 先取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; }
|
后记
这道题其实不算很难吧,主要就是推式子那里麻烦了一点,然后要用到快速幂,其他的也并不是很麻烦,不想用读入优化也是可以的,只是因为比赛的时候想优化一下(其实我还加了八聚氧只不过太长了所以就删掉了)。