[bzoj4926]皮皮妖的递推,又是一个牛逼题

题目描述

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(i1)=01成立
我们来尝试证明。
首先i=2时显然满足。
我们假设fm(i)fm(i1)=01成立。
那么可以推出f(i)f(i1)=01成立。
因为f(i)=ifm(i1)
f(i1)=i1fm(i2)
f(i)f(i1)=1(fm(i1)fm(i2))
后面部分为0或1,结论成立。
然后我们来说明,如果fx(i)fx(i1)=01成立
一定有fx+1(i)fx+1(i1)=01成立。
因为fx+1(i)=f(fx(i))=fx(i)fm(fx(i)1)
fx+1(i1)=f(fx(i1))=fx(i1)fm(fx(i1)1)
fx+1(i)fx+1(i1)=1(fm(fx(i)1)fm(fx(i1)1))
假如fx(i)=fx(i1),则显然后面部分为0,结论成立。
否则,一定有fx(i)fx(i1)=1,于是后面部分为0或1,结论成立。
最后又能推回原结论是对的。
可能大家直观觉得这并不是正常的归纳法,到底如何说明这些结论都对呢?
我们设这些结论分别为A1~m。
已知i=2时Am正确。
f(i)f(i1)=1(fm(i1)fm(i2))
注意之间这个式子。
i=k时A1的正确性,取决于i=k-1时Am的正确性。
fx+1(i)fx+1(i1)=1(fm(fx(i)1)fm(fx(i1)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(i1)=1
有了刚刚那个性质所以可以定义出这个h。
注意这个定义和下面这个定义不同:
设h(i)表示最大的x,满足fx(i)fx(i1)=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(i1)=1
fx1(f(i))fx1(f(i1))=1
又因为f(i)-f(i-1)=1,所以有h(f(i))=x-1。
如果我们求出了h,根据上面的f转移重写,可以这样计算f
f(i)=(i1j=1[h(j)<=m1])1
接下来的部分有点突破脑洞了。
我们来依靠打表找h的规律(我感觉直接想不太能想得到)
同样我们按照一些方法将这个表分行。
下面是m=4的情况:
这里写图片描述
可能你也没看出什么具体规律,只看出了它有规律QAQ。
我们这样描述它的规律:每一行全部数+1,然后对于每一个>=m的位置后面插入0,便得到了下一行。
我们把这个变化规律记作Trans(S)。
这个变化满足一个结论,假设S=S1+S2,有Trans(S)=Trans(S1)+Trans(S2),这个显然。
至于这个规律为何是对的,你知道f(i)=(i1j=1[h(j)<=m1])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)=(i1j=1[h(j)<=m1])1,我们得到下列算法:
递推出a序列,然后倒着来。
每次若n>=a[i],答案加上a[i-1],且n-=a[i]。
正确性也显然。

构造解法

来自出题人Monster_Yi。
我们尝试构造数列g。
满足以下性质:
n=ki=1g(ai)
f(n)=ki=1g(ai1)
就是这么神奇!
g序列的递推式为g(i)=g(i-1)+g(i-m)。
我们来考虑证明。
如果这个数列存在。
首先f(n)+fm(n1)=n
n=ki=1g(ai)
f(n)=ki=1g(ai1)
f2(n)=f(f(n))=ki=1g(ai2)
这样类推得到
fm(n)=f(fm1(n))=ki=1g(aim)
因此
ki=1g(aim)+g(ai1)=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);
}
阅读更多
版权声明:本文是蒟蒻写出来的,神犇转载也要说一声哦! https://blog.csdn.net/WerKeyTom_FTD/article/details/73065477
个人分类: 一般动规与递推
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭