Solution
从高到低DP。
设
fi,j,k,l
表示考虑了前
i
位,是否等于
设
然后枚举当前位直接转移就好啦。
using namespace std;
typedef long long ll;
inline char get(void) {
static char buf[100000], *S = buf, *T = buf;
if (S == T) {
T = (S = buf) + fread(buf, 1, 100000, stdin);
if (S == T) return EOF;
}
return *S++;
}
template<typename T>
inline void read(T &x) {
static char c; x = 0; int sgn = 0;
for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
if (sgn) x = -x;
}
int f[70][2][2][2], g[70][2][2][2];
ll n, m, K, ans;
int P, test;
template<typename T>
inline void Add(T &x, int a) {
x += a; while (x >= P) x -= P;
while (x < 0) x += P;
}
int main(void) {
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
read(test);
while (test--) {
read(n); read(m); read(K); read(P); n--; m--;
memset(f, 0, sizeof f);
memset(g, 0, sizeof g);
f[0][1][1][1] = 1; g[0][1][1][1] = 0;
for (int i = 0; i <= 63; i++)
for (int j = 0; j < 2; j++)
for (int k = 0; k < 2; k++)
for (int l = 0; l < 2; l++) {
if (!f[i][j][k][l]) continue;
int nn = (n >> (63 - i)) & 1,
mm = (m >> (63 - i)) & 1,
kk = (K >> (63 - i)) & 1;
for (int x = 0; x < 2; x++) {
if (j && x > nn) continue;
for (int y = 0; y < 2; y++) {
if (k && y > mm) continue;
int z = x ^ y;
if (l && z < kk) continue;
int nj = (j && x == nn),
nk = (k && y == mm),
nl = (l && z == kk);
Add(f[i + 1][nj][nk][nl], f[i][j][k][l]);
Add(g[i + 1][nj][nk][nl], g[i][j][k][l]);
Add(g[i + 1][nj][nk][nl], ((ll)z << (63 - i)) % P * f[i][j][k][l] % P);
}
}
}
K %= P; ans = 0;
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
for (int k = 0; k < 2; k++)
Add(ans, g[64][i][j][k] - K * f[64][i][j][k] % P);
Add(ans, P);
cout << ans << endl;
}
return 0;
}