储能表
将n, m分解为二进制,考虑一个log(n)层的trie树,n会在这颗trie树上走出了一个路径,因为 行数 $ \le n$,所以在n的二进制路径上,每次往1走的时候,与m计算贡献,m同样处理,$O(Tlog(n)log(m))$
当然可以数位dp, $f_{i, n, m, k}$分别代表考虑到第i位,与n, m, k的大小关系,不是很好写,就写了上面的方法。
1 #include <bits/stdc++.h> 2 #define rep(i, a, b) for (int i = a; i <= b; i++) 3 #define drep(i, a, b) for (int i = a; i >= b; i--) 4 #define REP(i, a, b) for (int i = a; i < b; i++) 5 #define pb push_back 6 #define mp make_pair 7 #define xx first 8 #define yy second 9 using namespace std; 10 typedef long long ll; 11 typedef pair<int, int> pii; 12 typedef unsigned long long ull; 13 const int inf = 0x3f3f3f3f; 14 const ll INF = 0x3f3f3f3f3f3f3f3fll; 15 template <typename T> void Max(T& a, T b) { if (b > a) a = b; } 16 //********************************* 17 18 ll n, m, k, mod; 19 20 ll POW(ll base, ll num) { 21 ll ret = 1; 22 while (num) { 23 if (num & 1) ret = ret * base % mod; 24 base = base * base % mod; 25 num >>= 1; 26 } 27 return ret; 28 } 29 30 ll calc(ll st, ll num) { 31 if (st < 0) { num += st; st = 0; } 32 if (num <= 0) return 0; 33 ll t1 = 2 * st + num - 1; 34 if (t1 & 1) num >>= 1; 35 else t1 >>= 1; 36 return t1 % mod * (num % mod) % mod; 37 } 38 ll solve(ll x, ll y, ll i, ll j) { 39 ll ret(0); 40 if (j > i) swap(i, j), swap(x, y); 41 ll z = x ^ (y ^ (y & ((1ll << i) - 1))); 42 ret = calc(z - k, 1ll << i); 43 return (ret * ((1ll << j) % mod)) % mod; 44 } 45 int main() { 46 /* 47 freopen("table.in", "r", stdin); 48 freopen("table.out", "w", stdout); 49 */ 50 int T; scanf("%d", &T); 51 while (T--) { 52 scanf("%lld%lld%lld%lld", &n, &m, &k, &mod); 53 54 ll x(0); 55 ll ans(0); 56 drep(i, 62, 0) if (n >> i & 1) { 57 ll y(0); 58 drep(j, 62, 0) if (m >> j & 1) { 59 (ans += solve(x, y, i, j)) %= mod; 60 y |= 1ll << j; 61 } 62 x |= 1ll << i; 63 } 64 65 printf("%lld\n", ans); 66 } 67 }