题解 AT3897 【ニワンゴくんとゲーム】
MSF_Akatsuki · · 题解
我的博客的该篇题解 (然而并没有更好的排版)
另外做本题是参考了国外的某博客
题意:求递推式
的第n项,数据范围
非常清楚地知道这是个矩阵乘法,然而并不会构矩阵。
我们发现求
我们这里将
接下来考虑如何完整地转移整个向量。对于向量的任何一个位置
接下来我们考虑什么情况会导致哪些位置的函数值改变。容易发现当某个数
但是有一个根本的问题没有解决:我们构造了矩阵,但是无法利用矩阵进行加速啊。
我们谈到,这个转移只跟
设从
考虑结合律,假设x最高位是第b位,基于转移只跟数末尾的0有关和矩阵乘法结合律,那么这个式子可以化作:
那么后半段方括号内就是
复杂度:
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
typedef long long LL;
const int MOD=1000000007;
LL kano()
{
char ch=getchar();LL w=0,u=1;
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')u=-1;
for(;ch>='0'&&ch<='9';ch=getchar())w=w*10+ch-'0';
return w*u;
}
LL R[65];
LL Q,n,m;
struct MATRIX
{
int a[65][65];
int n,m;
MATRIX(){n=m=0;memset(a,0,sizeof a);}
MATRIX(LL w)
{
n=m=60;memset(a,0,sizeof a);
for(LL i=0,j=w;i<60;i++,j=j>>1)
{
a[i][i]=1;
a[i+1][i]=j&1;
}
}
}p[60],ans;
MATRIX &operator *(const MATRIX &a,const MATRIX &b)
{
static MATRIX ans;
ans.n=a.n;ans.m=b.m;
memset(ans.a,0,sizeof ans.a);
for (int l=0;l<a.m;l++)
{
for(int i=0;i<ans.m;i++)
{
if(b.a[l][i]==0)continue;
for(int j=0;j<ans.n;j++)
{
ans.a[j][i]=(ans.a[j][i]+(LL)a.a[j][l]*b.a[l][i])%MOD;
}
}
}
return ans;
}
void work(LL n,int dep)
{
if(n==0)return;
if((n&R[dep])==R[dep])
{
ans=ans*p[dep+1];
return;
}
if(n&1)
{
work((1ll<<dep)-1,dep-1);
ans=ans*MATRIX((1ll<<(dep+1))-1);
}
work(n>>1,dep-1);
}
int main()
{
p[0]=MATRIX(0);
for(LL i=1,j=2;i<60;i++,j=j<<1)
{
p[i]=MATRIX(j-1);
p[i]=p[i]*p[i-1];
p[i]=p[i-1]*p[i];
}
Q=kano();
R[0]=1;
for(int i=1;i<=60;i++)R[i]=R[i-1]<<1|1;
while(Q--)
{
ans.n=1;ans.m=60;
for(int i=0;i<ans.m;i++)ans.a[0][i]=1;
n=kano()-1;
m=0;
for(int i=0;i<60;i++,n=n>>1)m=m<<1|(n&1);
n=m;
work(n,59);
printf("%d\n",ans.a[0][0]);
}
}