题目描述
YOUSIKI学习了递推,于是他请皮皮妖给他出道题,皮皮妖说:
f(1)=1,f(i)=i-f(i-1),求f(n)
YOUSIKI看了一眼把它秒切了,于是他要求皮皮妖加大难度,皮皮妖想了想,说:
f(1)=1,f(i)=i-f(f(i-1)),求f(n)
YOUSIKI看了两眼把它秒切了,于是他要求皮皮妖加大难度,皮皮妖想了想,说:
f(1)=1,f(i)=i-f(f(f(i-1))),求f(n)
YOUSIKI看了三眼把它秒切了,于是他要求皮皮妖加大难度,皮皮妖想了想,说:
…
…
…
YOUSIKI看了m眼,但是没有能秒切,于是他找到你,请你帮他解决这个问题。
非构造解法
来自mcfx,本篇基本参考他的博客。我写的比较详细,可能有点傻逼T_T。
首先我们设
fx(i)表示f(f(f……(f(i)))))这样一共嵌套了x层
有一个结论,对于任意x有
fx(i)−fx(i−1)=0或1
成立
我们来尝试证明。
首先i=2时显然满足。
我们假设
fm(i)−fm(i−1)=0或1
成立。
那么可以推出
f(i)−f(i−1)=0或1
成立。
因为
f(i)=i−fm(i−1)
f(i−1)=i−1−fm(i−2)
f(i)−f(i−1)=1−(fm(i−1)−fm(i−2))
后面部分为0或1,结论成立。
然后我们来说明,如果
fx(i)−fx(i−1)=0或1
成立
一定有
fx+1(i)−fx+1(i−1)=0或1
成立。
因为
fx+1(i)=f(fx(i))=fx(i)−fm(fx(i)−1)
fx+1(i−1)=f(fx(i−1))=fx(i−1)−fm(fx(i−1)−1)
fx+1(i)−fx+1(i−1)=1−(fm(fx(i)−1)−fm(fx(i−1)−1))
假如
fx(i)=fx(i−1)
,则显然后面部分为0,结论成立。
否则,一定有
fx(i)−fx(i−1)=1
,于是后面部分为0或1,结论成立。
最后又能推回原结论是对的。
可能大家直观觉得这并不是正常的归纳法,到底如何说明这些结论都对呢?
我们设这些结论分别为A1~m。
已知i=2时Am正确。
f(i)−f(i−1)=1−(fm(i−1)−fm(i−2))
注意之间这个式子。
i=k时A1的正确性,取决于i=k-1时Am的正确性。
fx+1(i)−fx+1(i−1)=1−(fm(fx(i)−1)−fm(fx(i−1)−1))
然后回到这条式子。
i=k时Ax+1的正确性,取决于i=k时Ax的正确性。注意到还用到了Am。
然后因为此时一定证出来i<=kA1都成立,于是有f(i)<=i。
那么还取决于的是某个
j<k
时Am的正确性。
这样从边界开始推。
显然A1~m在任意i处都成立吧?
然后我们用长篇大论说明了一个很容易猜到的结论是正确的……
接下来证明下一个结论:
fx(i)>=fx+1(i)
即
fx(i)>=f(fx(i))
因为之前证明了f(i)<=i,所以这个结论也就证明了。
接下来,我们设h(i)表示最大的x,满足任意j属于[1,x]都有
fj(i)−fj(i−1)=1
有了刚刚那个性质所以可以定义出这个h。
注意这个定义和下面这个定义不同:
设h(i)表示最大的x,满足
fx(i)−fx(i−1)=1
因为注意到如果f(i)=f(i-1),这个x取到正无穷都可以。
但是之前那个定义写的是[1,x],因此x可以取0。
这就是两种定义的区别。
然后我们发现f的转移可以重写:
f(1)=f(2)=1。
对于i>=3,若h(i-1)>=m,则f(i)=f(i-1)且h(i)=0。
否则f(i)=f(i-1)+1且h(i)=h(f(i))+1。
对于第一条显然。
对于第二条,设h(i)=x。
fx(i)−fx(i−1)=1
fx−1(f(i))−fx−1(f(i−1))=1
又因为f(i)-f(i-1)=1,所以有h(f(i))=x-1。
如果我们求出了h,根据上面的f转移重写,可以这样计算f
f(i)=(∑i−1j=1[h(j)<=m−1])−1
接下来的部分有点突破脑洞了。
我们来依靠打表找h的规律(我感觉直接想不太能想得到)
同样我们按照一些方法将这个表分行。
下面是m=4的情况:
可能你也没看出什么具体规律,只看出了它有规律QAQ。
我们这样描述它的规律:每一行全部数+1,然后对于每一个>=m的位置后面插入0,便得到了下一行。
我们把这个变化规律记作Trans(S)。
这个变化满足一个结论,假设S=S1+S2,有Trans(S)=Trans(S1)+Trans(S2),这个显然。
至于这个规律为何是对的,你知道
f(i)=(∑i−1j=1[h(j)<=m−1])−1
,于是你可以维护指针表示f(i),然后你发现每一行的一些位置指针刚好指到左上方位置。这样模拟下来,这个变化规律确实正确。请大家自己手玩,这个规律没有很好的方法说明它对。
接下来,我们知道>=m的没有差别,如果我们统一记作m,会发生什么?(这也是很脑洞)
可能你看到了很明显的一个规律了。
我们直接给出最终规律,设第i行(这里我们的行数从0开始计数)为S[i],如果i>m,有S[i]=S[i-1]+S[i-m]。
考虑证明。
i=m+1时容易发现是满足的。
对于i>m+1的情况。
S[i]=Trans(S[i-1])=Trans(S[i-2])+Trans(S[i-m-1])=S[i-1]+S[i-m]。
于是归纳的证明了。
现在我们设a[i]表示第i行数的个数,则a[i]=a[i-1]+a[i-m](边界全都是1)又有一个结论:
第i行<=m-1的数的个数是a[i-1]。
为啥呢?
假设第i行有x个>=m的数吧。
在第i-1行全部加1后,这些数后面都会插0,因此会多出来x个。
也就是说a[i]-x=第i行<=m-1的数个数。
而显然a[i]-x=a[i-1]。
因此得证。
于是根据
f(i)=(∑i−1j=1[h(j)<=m−1])−1
,我们得到下列算法:
递推出a序列,然后倒着来。
每次若n>=a[i],答案加上a[i-1],且n-=a[i]。
正确性也显然。
构造解法
来自出题人Monster_Yi。
我们尝试构造数列g。
满足以下性质:
若
n=∑ki=1g(ai)
则
f(n)=∑ki=1g(ai−1)
就是这么神奇!
g序列的递推式为g(i)=g(i-1)+g(i-m)。
我们来考虑证明。
如果这个数列存在。
首先
f(n)+fm(n−1)=n
n=∑ki=1g(ai)
则
f(n)=∑ki=1g(ai−1)
f2(n)=f(f(n))=∑ki=1g(ai−2)
这样类推得到
fm(n)=f(fm−1(n))=∑ki=1g(ai−m)
因此
∑ki=1g(ai−m)+g(ai−1)=∑ki=1g(ai)
令g(i)=g(i-1)+g(i-m)即可。
不得不说,感觉这个题挺牛逼的。。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
ll n,ans,a[5000005];
int i,j,k,l,t,m,top;
int main(){
scanf("%lld%d",&n,&m);
fo(i,0,m) a[i]=1;
i=m+1;
top=m;
while (1){
a[i]=a[i-1]+a[i-m];
if (a[i]>n) break;
top=i;
i++;
}
fd(i,top,1)
if (n>=a[i]) n-=a[i],ans+=a[i-1];
printf("%lld\n",ans);
}