题目
3329:Xorequ
TimeLimit:1Sec
MemoryLimit:256MB
Description
给定正整数
N
现有如下方程
其中
⨁
表示按位异或
任务如下:
1.求出
1
~
2.求出
1
~
Input
第一行一个正整数,表示数据组数
T
,接下来
每行一个正整数
N
Output
2T
行
第
2i−1
行表示第
i
个数据中问题一的解
第
SampleInput
1
1
SampleOutput
1
2
HINT
x=1
与
x=2
都是原方程的根
注意第一个问题的解
不要模
109+7
1≤N≤1018
1≤T≤1000
题解
因为
N≤1018
,所以可以想到时空复杂度约为
O(logN)
。
这样子,有经验的老人家们就能想到矩阵乘法,进而想到矩阵优化。
然而,矩阵优化的前提是动态规划……然而,我们并未想到动态规划。
观察方程
x⨁3x=2x
,可以变为
x⨁2x=3x
。
又因为
x+2x=3x
,所以可以得到
x
的二进制数没有连续的
至于为什么呢?博主也不知道……就稍微水水地证明一下。
x+y
为进位加法,
x⨁y
为不进位加法。
所以当
x+y=x⨁y
时,
x
与
又因为
接下来就是比较明显的数位DP。
f(i,j)
表示一个
i
位的数以
那么
f(0,1)=0,f(0,0)=1,f(i,1)=f(i−1,0),f(i,0)=f(i−1,0)+f(i−1,1)
。
仔细观察发现
j=0
和
j=1
时刚好错位。
所以可以把它合成一维。
g(i)=f(i,0)=f(i+1,1)
,那么
g(0)=g(1)=1,g(i)=g(i−1)=g(i−2)
。
因为这个DP方程是线性的,所以可以矩阵优化。
那么第一问用数位DP,第二问用矩阵优化,时空复杂度均为
O(logN)
。
(没有学过矩阵优化的朋友们建议写一下模板题
luogu1962
)
总结
对于
N≤1018
这样的数据范围的题目,可以想到时空复杂度约为
O(logN)
。
这种时候就要想到倍增类型的做法,如本题的数位DP和矩阵优化。
为了使用矩阵优化,就要想办法转移成广义斐波那契数列。
标程
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll MOD=1000000007;
const ll N=65;
ll t, n, s[N], f[N];
struct matrix{
ll a[2][2];
matrix(){memset(a, 0, sizeof(a));}
matrix operator *(const matrix& b)const
{
matrix c;
for (ll i=0; i<=1; i++)
for (ll j=0; j<=1; j++)
for (ll k=0; k<=1; k++)
c.a[i][j]=(c.a[i][j]+1LL*a[i][k]*b.a[k][j]%MOD)%MOD;
return c;
}
};
matrix power(matrix a, ll b)
{
matrix c=a; b--;
while (b)
{
if (b&1) c=c*a;
b>>=1; a=a*a;
}
return c;
}
void init()
{
s[1]=f[0]=f[1]=1;
for (ll i=2; i<=60; i++)
{
s[i]=s[i-1]<<1;
f[i]=f[i-1]+f[i-2];
}
}
void work1(ll x)
{
ll ret=0, w=60; x++;
while (s[w]>x) w--;
ll cur=(x>>(w-1)), pre;
for (ll i=1; i<w; i++)
ret+=f[i-1];
pre=cur; x^=(cur<<(w-1));
for (ll i=w-1; i; i--)
{
cur=(x>>(i-1));
if (cur) ret+=f[i];
if (cur && pre) break;
pre=cur; x^=(cur<<(i-1));
}
printf("%lld\n", ret);
}
void work2(ll x)
{
matrix ans; ans.a[0][0]=ans.a[0][1]=ans.a[1][0]=1;
ans=power(ans, x+1); printf("%lld\n", ans.a[0][0]);
}
int main()
{
init(); scanf("%lld", &t);
while (t--)
{
scanf("%lld", &n);
work1(n); work2(n);
}
return 0;
}