【数位DP】luogu_P7415 Count the Cows G
题意
对于每一个满足\(x≥0\)以及\(y≥0\)的方格\((x,y)\),当对于所有整数\(k≥0\),\(\left\lfloor \frac{x}{3^k}\right\rfloor\)和\(\left\lfloor \frac{y}{3^k}\right\rfloor\)除以三的余数的奇偶性均相同时,有一头奶牛位于\((x,y)\)。
换言之,两个余数均为奇数(均等于\(1\)),或均为偶数(均等于\(0\)或\(2\))。
例如,满足\(0≤x,y<9\)的方格中,包含奶牛的方格在下图中用 1 表示。
x
012345678
0 101000101
1 010000010
2 101000101
3 000101000
y 4 000010000
5 000101000
6 101000101
7 010000010
8 101000101
他进行了 QQ 个询问,每个询问由三个整数\(x_i,y_i,d_i\)组成。
对每个询问,FJ想要知道有多少奶牛位于\((x_i,y_i)\)至\((x_i+d_i,y_i+d_i)\)的对角线上的方格内(包括两端)。
思路
先对一个\(k\)进行分析。
\(x=3^ka+r_0\)
\(y=3^kb+r_1\)
因为研究\(a\),\(b\)的关系,再提一个\(3\)。
\(x=3^k(3p_x+q_x)+r_0\)
\(y=3^k(3p_y+q_y)+r_1\)
展开即为:
\(x=3^{k+1}p_x+3^kq_x+r_0\)
\(y=3^{k+1}p_y+3^kq_y+r_1\)
对于\(r_0\),\(r_1\),我们也可以将它们分成包含若干\(3\)的次幂的式子。
最后变成三进制的形式。
\(x = \sum_{k = 0} 3^k a_{k}\)
\(y = \sum_{k = 0} 3^k b_{k}\)
在上面分析过,单独对于每个k来看,\(a_k\)与\(b_{k}\)要\(\% 2\)同余。
故满足条件的\((x,y)\),\(x、y\)在三进制情况下的每位\(\% 2\)同余。
用数位dp计算即可。
代码
#include <cstdio>
#include <cstring>
const int n = 38;
int q;
int X[39], Y[39], D[39], v[39][2][2][2];
long long f[39][2][2][2];
int check(int a, int b) {
if (a < 0 || a > 2 || b < 0 || b > 2)
return 0;
return (a & 1) == (b & 1);
}
long long dp(int dep, int a, int b, int flag) {//当前数位,是否向dep+1进位,,d有没有顶到
if (dep < 0)
return !a && !b;
if (v[dep][a][b][flag])
return f[dep][a][b][flag];
v[dep][a][b][flag] = 1;
long long res = 0;
for (int i = 0; i <= (flag ? D[dep] : 2); i++)//flag的体现
for (int j = 0; j <= 1; j++)//枚举下一位要不要向当前位进位
for (int k = 0; k <= 1; k++)
if (check(X[dep] - 3 * a + i + j, Y[dep] - 3 * b + i + k))
res += dp(dep - 1, j, k, flag & (D[dep] == i));
return f[dep][a][b][flag] = res;
}
int main() {
scanf("%d", &q);
long long d, x, y;
while (q--) {
memset(v, 0, sizeof(v));
scanf("%lld %lld %lld", &d, &x, &y);
for (int i = 0; i <= n; i++) {
X[i] = x % 3;
x /= 3;
Y[i] = y % 3;
y /= 3;
D[i] = d % 3;
d /= 3;
}
printf("%lld\n", dp(n, 0, 0, 1));
}
}

浙公网安备 33010602011771号