题面
传送门
大概题意就是给出一个d和m。
求满足以下条件的数组的数量模m。
- 数组长度大于等于1
- 数组所有元素大于等于1,小于等于d
- 数组严格递增
- 衍生数组( b1=a1, ∀i>1,bi=bi−1⊕ai)严格递增
思路
定义f(x)为x的下一位最小值
打表可得f(1)=2,f(2)=f(3)=4,f(4)=f(5)=f(6)=f(7)=8…
至于原因就是一个数若异或上自己最高也是1的那么那一位就会被抵消从而小于自己。比如2的下一位不能3因为他们都具有2的1次,异或后便无,所以会小于2的1次,所以2的下一位不能是3,其他同理。而异或上比自己高一位的,那一位必不会抵消所以一定大于本身。
思考一下
如果数组长度为1,那么就是1-d所有数。
…
如果数组长度为n,那么则是在所有长度为n-1的数组的基础上添加一个可行的数。
那么我们只需记录结尾在f(i)里的数有多少个,就可以求得个数*可行数,再转移到新结尾即可
而又因为每次下一个数都是2的幂次的跳,所以长度不会超过log。
下一个可行数个数怎么计算,假设当前数x的层数为i(2i<=x<2i+1),则下一位至少要大于2i+1,所以个数为d-2i+1+1。
如何转移,如果d无穷大,那么第i层会得到2i个数,因为d不是无穷大,所以只有最后一层要特殊处理一下,个数为d-2i+1+1。
对了 避免转移出错,应该倒序遍历。
复杂度大概在O(log3N)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long, long> pll;
const int maxn = 3e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
ll t, d, m;
ll dep[32];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> t;
while (t--) {
cin >> d >> m;
for (ll i = 0; i < 32; ++i) {
if ((1ll << i + 1) > d) {
dep[i] = d - (1 << i) + 1;
break;
}
dep[i] = (1 << i);
}
ll ans = d;
int times = 32;
while (times--) {
for (int i = 31; i >= 0; --i) {
if (d - (1ll << (i + 1)) + 1 <= 0) continue;
ans += dep[i] * (d - (1ll << i + 1) + 1) % m;
ll k = i + 1;
while ((1ll << (k + 1)) <= d) {
dep[k] += (1 << k) * dep[i];
dep[k] %= m;
k++;
}
dep[k] += (d - (1 << k) + 1) * dep[i];
dep[k] %= m;
dep[i] = 0;
}
}
cout << ans % m << endl;
}
return 0;
}