思路
异或又叫做半加,它与和加法的区别就是不会进位。可知
(a+b=(a⊕b)+2(a&b))
(2(a&b))就是进位的部分的值。
对于这一题,(b_i=b_{i-1}⊕a_i)可以写成(b_i=b_{i-1}+a_i-2(a_i&b_{i-1}))。
因为(b_i)是单调递增的,所以有(b_i-b_{i-1}=a_i-2(a_i&b_{i-1})>0),即需要满足(a_i>2(a_i&b_{i-1}))。
如果(a_i&b_{i-1})这一项最高位的1和(a_i)的相等,那么(2(a_i&b_{i-1}))必定大于(a_i)。
所以(a_i&b_{i-1})最高位的1低于(a_i)的,即(b_{i-1})的最高位低于(a_i)的。
因为(b_{i-1}=a_{1}⊕...⊕a_{i-1}),由归纳法可知道(a_i)的最高位的1所在的位置是单调递增的。
而且只要保证(a_i)位数严格递增,就必定满足(a_i>2(a_i&b_{i-1}))。当(a_i)位数为n时,它有(2^{n-1})种情况。
所以按照位数递增来dp就好了。复杂度是(O((log_2n)^3))。未满最高位的部分单独处理。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 50;
ll dp[N][N];
void work(int n, ll m) {
for(int i = 1; i <= n; i++)
dp[i][i] = 1;
for(int b = 1; b <= n; b++) { //终点的位数
for(int a = b - 1; a >= 1; a--) { //起点的位数
dp[a][b] = 0;
for(int i = a + 1; i <= b; i++) {
dp[a][b] += (1ll << (i - 1)) * dp[i][b] % m;
dp[a][b] %= m;
}
}
}
}
int main() {
ios::sync_with_stdio(false);
int t;
cin >> t;
while(t--) {
ll d, m;
cin >> d >> m;
int cnt = 0;
ll td = d;
while(td) {
cnt++;
td >>= 1;
}
work(cnt - 1, m);
ll ans = 0;
ll k = d - (1 << (cnt - 1)) + 1; //未满的部分单独处理
for(int i = 1; i <= cnt - 1; i++) {
for(int j = i; j <= cnt - 1; j++) {
ans += (((1ll << (i - 1)) * dp[i][j] % m) * k) % m;
ans %= m;
ans += ((1ll << (i - 1)) * dp[i][j] % m) % m;
ans %= m;
}
}
cout << (ans + k) % m << endl;
}
}
Update
看了官方题解,才发现dp麻烦了。对于每个位数v,有区间([2v,min(2(v+1)−1,d)])那么多种数可以选择,即((min(2(v+1)−1,d)−2v+1)+1)种选择(不选这个区间的数也是一种选择,故加1)。全部乘起来再减1(减去全部不选的方案)就是答案。这样处理好简单。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 50;
int main() {
ios::sync_with_stdio(false);
int t;
cin >> t;
while(t--) {
int n, m;
cin >> n >> m;
ll ans=1;
for(int i = 0; i < N; i++) {
if(n < (1 << i)) break;
ans = ans * (min((1 << (i+1)) - 1, n) - (1 << i) + 2) % m;
}
ans--;
if(ans < 0) ans += m;
cout << ans << endl;
}
}