然后这道题就解决了。代码不长,看起来有60行左右的原因是很多都是复制的。所以实际只有几个递推式是核心内容。
```cpp
#include<iostream>
#include<cstring>
using namespace std;
long long l[60][2],r[60][2],m[60][2];
#define mod 1000000007
long long fl(long long n,int step){
if(l[step][n&1ll]){
return l[step][n&1ll];
}
if(n==1ll){
return 1ll;
}
long long left,right=n>>1ll;
left=n-right;
l[step][n&1ll]=(fl(left,step+1)+fl(right,step+1ll)+right-1ll)%mod;
return l[step][n&1ll];
}
long long fr(long long n,int step){
if(r[step][n&1ll]){
return r[step][n&1ll];
}
if(n==1ll){
return 1ll;
}
long long left,right=n>>1ll;
left=n-right;
r[step][n&1ll]=(fr(left,step+1)+left+fr(right,step+1)-1ll)%mod;
return r[step][n&1ll];
}
long long fm(long long n,int step){
if(m[step][n&1ll]){
return m[step][n&1ll];
}
if(n==1ll){
return 1ll;
}
long long left,right=n>>1ll;
left=n-right;
m[step][n&1ll]=(fm(left,step+1)+fm(right,step+1)+fr(left,step+1)*(right%mod)+fl(right,step+1)*(left%mod)-1ll)%mod;//left,right在做乘法时一定要先模mod,因为它们可能很大。
return m[step][n&1ll];
}
int main(){
long long t,i,n,t1,t2,t3;
cin>>t;
for(i=0;i<t;i++){
memset(l,0,sizeof(l)); //与普通记忆化不同的是,一定要清空,因为对于不同的n,f[step][odd]的含义不一样了。
memset(r,0,sizeof(r));
memset(m,0,sizeof(m));
scanf("%lld",&n);
cout<<fm(n,0)<<endl;
}
}
```
## 一些题外话
官方题解讲得有那么一定的清楚,本人只看懂了"算贡献"3个字。于是照着这3个字思考了一周。所以题解中有我的思考过程,可能会很啰嗦。
$\color{green}\text{Happy new year!}