题目传送门
分析:
不是很懂,一顿胡乱找规律2333
先写个暴力,设(f[i][j])表示目前还有(i)个石子,目前这一步最多取(j)个,先手是否必胜
看一下转移:
[f[i][j]=![&_{k=1}^{j}f[i-k][2k]]
]
不知道与和怎么写,直接写一个(&)顶一下吧2333
光看这个式子就可以发现一些性质:如果(f[i][j])为必败态,那么(f[i][k](k<j))也一定必败
那么设(a_i)表示使(f[i][a_i])为必败态的最大的值
打表出来找一下规律。。。从(a_0)开始:
(INF,0,1,2,0,4,0,1,7,0,1,2,0,12,0,1,2,0,4,0,1,20....)
发现其中某一些(a_i=i-1),之间间隔的数字是整个数列的前缀的一部分
把(a_i=i-1)的数全部提出来。。。设第(i)个数为(g_i),从(g_0)开始
(0,1,2,4,7,12,20,33....)
发现(g_i=g_{i-1}+g_{i-2}+1)
再算一下(g_i)之前每一个(g_j)的出现次数。。。(假设(i)为7)
(13,8,5,3,2,1,1)
发现是倒序的斐波那契数列
规律就找到了。。。
斐波那契数列和上面的(g)增长速度都是指数级别的
单次复杂度为(O(logN))
总复杂度(O(TlogN))
这规律真离谱
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<iostream>
#include<map>
#include<string>
#define maxn 100005
#define INF (1ll<<60)
using namespace std;
inline long long getint()
{
long long num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
long long k,N;
long long f[maxn],fib[maxn],cnt;
long long ans;
inline void solve(int x)
{
if(k>f[x])ans++;
for(int i=0;i<x;i++)if(k>f[i])ans+=fib[x-i];
}
int main()
{
f[0]=0,f[1]=1,cnt=1,fib[0]=0,fib[1]=1;
while(1)
{
cnt++;
f[cnt]=f[cnt-1]+f[cnt-2]+1;
fib[cnt]=fib[cnt-1]+fib[cnt-2];
if(f[cnt]>INF)break;
}
int T=getint();
while(T--)
{
k=getint(),N=getint();ans=0;
if(N==1){printf("0
");continue;}
N-=2;
for(int i=cnt;i>=0;i--)if(N>=f[i])solve(i),N-=f[i]+1;
printf("%lld
",ans);
}
}