LOJ6274 数字 【dp套dp】
题目描述:求 \(\text{card}(\{x\and y|x\or y=T,x\in [L_x,R_x],y\in [L_y,R_y]\})\)。
数据范围:\(T,L_x,R_x,L_y,R_y\le 2^{60}\)
dp套dp经典题?
我们考虑如何验证 \(x\and y=V\) 是否可能成立,这个东西用数位dp做,设 \(f[i][a][b][c][d]\) 表示第 \(i\) 位上,\(x,y\) 与 \(L_x,R_x;L_y,R_y\) 的关系(这里的关系是类似 limit 的“是否顶到上/下界”)是否可行。由于这 \(16\) 个数都是 bool 值,所以我们可以用一个 \(16\) 位二进制数压到一起,设 \(dp[i][S]\) 表示第 \(i\) 位上,\(f\) 的 \(16\) 个值为 \(S\) 的情况下,有多少个 \(V\) 满足。暴力枚举转移即可,时间复杂度 \(O(2^{21}\log T)\),当然这只是一个粗略的上界。
最后答案应该是 \(\sum_{i=1}^{2^{16}-1}dp[0][i]\),因为 \(dp[0][0]\) 表示任何情况下都不符合的情况。
#include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef unsigned long long LL;
template<typename T>
inline void read(T &x){
int ch = getchar(); x = 0;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
LL T, Lx, Rx, Ly, Ry, dp[61][65536], ans;
inline int sta(int a, int b, int c, int d){return a | b << 1 | c << 2 | d << 3;}
int main(){
read(T); read(Lx); read(Rx); read(Ly); read(Ry);
dp[60][1 << sta(1,1,1,1)] = 1;
for(Rint i = 59;~i;-- i){
int t = T >> i & 1, lx = Lx >> i & 1, rx = Rx >> i & 1, ly = Ly >> i & 1, ry = Ry >> i & 1;
for(Rint S = 0;S < 65536;++ S) if(dp[i + 1][S])
for(Rint v = 0;v < 2;++ v){
int nxt = 0;
for(Rint SS = 0;SS < 16;++ SS) if(S >> SS & 1){
int a = SS & 1, b = SS >> 1 & 1, c = SS >> 2 & 1, d = SS >> 3 & 1;
for(Rint x = 0;x < 2;++ x)
for(Rint y = 0;y < 2;++ y)
if((x | y) == t && (x & y) == v && (!a || x >= lx) && (!b || x <= rx) && (!c || y >= ly) && (!d || y <= ry))
nxt |= 1 << sta(a && x == lx, b && x == rx, c && y == ly, d && y == ry);
}
dp[i][nxt] += dp[i + 1][S];
}
}
for(Rint S = 1;S < 65536;++ S) ans += dp[0][S];
printf("%llu\n", ans);
}