题意:
给定正整数
n
n
n, 现有如下方程
x
⊕
3
x
=
2
x
x\oplus3x=2x
x⊕3x=2x
任务如下:
- 求出小于等于 n n n的正整数中,有多少个是该方程的解
- 求出小于等于 2 n 2^{n} 2n的正整数中,有多少个是该方程的解,模 1 0 9 + 7 10^9+7 109+7
解题思路:
问题一:
- 将原式子变换 x ⊕ 2 x = 3 x = x + 2 x x\oplus2x=3x=x+2x x⊕2x=3x=x+2x
- x x x x o r xor xor y y y ≤ \le ≤ x x x o r or or y y y ≤ \le ≤ x + y x + y x+y
- 因此 x x x o r or or y y y = = = x + y x + y x+y,所以 x x x a n d and and 2 x = 0 2x=0 2x=0即x转换为2进制后没有相邻的1,数位dp即可
ll dfs(int pos, int sta, bool limit) {
if (pos == -1) return 1;
if (!limit && dp[pos][sta] != -1) return dp[pos][sta];
int up = limit ? digit[pos] : 1;
ll sum = 0;
for (int i = 0; i <= up; i++) {
if (sta && i == 1) continue;
sum += dfs(pos - 1, i == 1, limit && i == digit[pos]);
}
if (!limit) dp[pos][sta] = sum;
return sum;
}
ll solve(ll x) {
int cnt = 0;
while (x) {
digit[cnt++] = x % 2, x /= 2;
}
ll ans = dfs(cnt - 1, 0, true);
return ans;
}
问题二:
- 询问 [ 1 , 2 n ] [1,2^n] [1,2n]有多少个不连续的两个1
- 显然斐波那契数列(看题解显然…)
- 比如说 f ( n ) f(n) f(n),第n位取1,那么第n-1位只能取0,所以要加上 f ( n − 2 ) f(n-2) f(n−2),第n位取0,那么第n-1位随便取,所以要加上 f ( n − 1 ) f(n-1) f(n−1)
- f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n−1)+f(n−2)
- 因为n很大,所以要矩阵加速,具体矩阵加速详情可看**luogu P1962**
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll Mod = 1e9 + 7;
const int maxn = 1e2;
ll n, dp[maxn][2]; // dp[i][0]代表i位选0的总方案数,。。。
int T, digit[maxn];
struct Matrix {
ll a[4][4];
Matrix operator * (const Matrix &b) const {
Matrix re;
memset(re.a, 0, sizeof(re.a));
for (int i = 1; i <= 2; i++)
for (int k = 1; k <= 2; k++)
for (int j = 1; j <= 2; j++)
re.a[i][j] = (re.a[i][j] + a[i][k] * b.a[k][j]) % Mod;
return re;
}
}ans, st;
ll read() {
ll X = 0, p = 1; char c = getchar();
for (; c > '9' || c < '0'; c = getchar()) if (c == '-') p = -1;
for (; c <= '9' && c >= '0'; c = getchar()) X = X * 10 + c - '0';
return X * p;
}
ll dfs(int pos, int sta, bool limit) {
if (pos == -1) return 1;
if (!limit && dp[pos][sta] != -1) return dp[pos][sta];
int up = limit ? digit[pos] : 1;
ll sum = 0;
for (int i = 0; i <= up; i++) {
if (sta && i == 1) continue;
sum += dfs(pos - 1, i == 1, limit && i == digit[pos]);
}
if (!limit) dp[pos][sta] = sum;
return sum;
}
ll solve(ll x) {
int cnt = 0;
while (x) {
digit[cnt++] = x % 2, x /= 2;
}
ll ans = dfs(cnt - 1, 0, true);
return ans;
}
void print(ll x) {
memset(dp, -1, sizeof(dp));
memset(st.a, 0, sizeof(st.a));
memset(ans.a, 0, sizeof(ans.a));
st.a[1][1] = st.a[1][2] = st.a[2][1] = 1;
ans.a[1][1] = 2, ans.a[1][2] = 1;
printf("%lld\n", solve(x) - 1); // 因为多计算了个0也是解,但题目要求正整数
x -= 1;
for (; x; x >>= 1, st = st * st)
if (x & 1) ans = ans * st;
printf("%lld\n", (ans.a[1][1] % Mod + Mod) % Mod);
}
int main() {
T = read();
while (T--) print(read());
return 0;
}