Generational Triplets
Generational Triplets
数位dp,位运算转换
cf2100
题目大意
给定整数 \(n\),求满足以下条件的整数三元组 \((a,b,c)\) 的数量:
- \(1 \le a < b < c \le n\)
- \(a,b,c\) 成等差数列,即 \(b-a = c-b\)
- \(a \oplus b \oplus c = 0\),其中 \(\oplus\) 表示按位异或
答案对 \(10^9+7\) 取模。
问题转化
由 \(a \oplus b \oplus c = 0\) 可得 \(b = a \oplus c\)。
又因为 \(a,b,c\) 成等差数列,所以 \(b = \frac{a+c}{2}\)。
于是得到:
利用恒等式 \(a+c = (a \oplus c) + 2(a \& c)\),代入上式:
化简得:
因此,原问题等价于:统计满足 \(1 \le a < c \le n\) 且 \(a \oplus c = 2(a \& c)\) 的整数对 \((a,c)\) 的数量。
每一个这样的 \((a,c)\) 唯一对应一个合法的三元组 \((a,b,c)\),其中 \(b = a \oplus c\)。
逐位分析
设 \(a\) 和 \(c\) 的第 \(i\) 位分别为 \(a_i, c_i\)。
由 \(a \oplus c = 2(a \& c)\) 可知,第 \(i\) 位的异或结果等于第 \(i-1\) 位的与结果:
也就是说,当前位的异或值由低一位的与值决定。
反过来,从高位向低位枚举时,当前位的与值必须等于高一位的异或值。
这样我们就把高低位之间的依赖关系反转了过来,使得每一位的决策只依赖于更高位的信息,满足无后效性,可以使用数位 DP。
数位 DP 设计
从高位到低位(第 \(60\) 位到第 \(0\) 位)枚举 \(a\) 和 \(c\) 的每一位。
因为 \(n \le 10^{18} < 2^{60}\),所以从第 \(60\) 位开始足够。
状态定义
- \(pos\):当前处理的二进制位(从 \(60\) 递减到 \(0\))。
- \(tight\):\(c\) 是否受到 \(n\) 的上限约束。
若为 \(1\),说明 \(c\) 的前缀与 \(n\) 相同,当前位 \(c\) 不能超过 \(n\) 的当前位;
若为 \(0\),说明 \(c\) 已经小于 \(n\),当前位 \(c\) 可以任意取 \(0\) 或 \(1\)。 - \(tightA\):\(a\) 是否已经严格小于 \(c\)。
若为 \(1\),说明 \(a\) 和 \(c\) 的前缀完全相同,当前位 \(a\) 不能大于 \(c\);
若为 \(0\),说明之前已经出现 \(a < c\),后续位 \(a\) 可以任意取。 - \(need\):当前位 \(a \& c\) 必须等于的值,由高一位的 \(a \oplus c\) 传递下来。
状态转移
对于当前位 \(pos\),令 \(up = tight ? ((n >> pos) \& 1) : 1\)。
枚举 \(c\) 从 \(0\) 到 \(up\),再枚举 \(a\) 从 \(0\) 到 \(t\),其中 \(t = tightA ? c : 1\)。
必须满足:
然后计算:
- \(newneed = a \oplus c\)(传递给下一位作为新的 \(need\))
- \(ntight = tight \&\& (c == up)\)
- \(nA = tightA \&\& (a == c)\)
递归调用:
累加后对 \(10^9+7\) 取模。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 1e9 + 7;
ll dp[66][2][2][2];
ll n;
ll dfs(int pos, int tight, int tightA, int need) {//pos位,tight n对c的限制,tightA c对a的限制,need 上一位XOR值
if(pos < 0) return (!tightA && need == 0) ? 1 : 0;
if(dp[pos][tight][tightA][need] != -1) return dp[pos][tight][tightA][need];
int up = tight ? ((n >> pos) & 1) : 1;
ll res = 0;
for(int c = 0; c <= up; c ++) {
int t = tightA ? c : 1;
for(int a = 0; a <= t; a ++) {
if((a & c) != need) continue;
int newneed = a ^ c;
int ntight = (tight && c == up);
int nA = (tightA && a == c);
res += dfs(pos - 1, ntight, nA, newneed);
res %= MOD;
}
}
dp[pos][tight][tightA][need] = res;
return res;
}
void solve() {
cin >> n;
memset(dp, -1, sizeof(dp));
cout << dfs(60, 1, 1, 0) << "\n";
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int _ = 1; cin >> _;
while(_--) solve();
return 0;
}

浙公网安备 33010602011771号