数位DP
记录两个模板题
(CF276D Little Girl and Maximum XOR)
A little girl loves problems on bitwise operations very much. Here's one of them.
You are given two integers \(l\) and \(r\) . Let's consider the values of \(a ^ b\) for all pairs of integers \(a\) and \(b\) \((l \le a \le b \le r)\) . Your task is to find the maximum value among all considered ones.
Expression \(x ^ y\) means applying bitwise excluding or operation to integers \(x\) and \(y\) . The given operation exists in all modern programming languages, for example, in languages C++ and Java it is represented as "^", in Pascal — as «xor».
Input
The single line contains space-separated integers \(l\) and \(r\) ( \(1 \le l \le r \le 10^{18}\) ).
Please, do not use the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams or the %I64d specifier.
Output
In a single line print a single integer — the maximum value of \(a ^ b\) for all pairs of integers \(a\) , \(b\) ( \(l \le a \le b \le r\) ).
此题不是计数问题,因此不可以用前缀和的思想直接用f(r) - f(l - 1)
的方式去求。我们可以对上下界(\(l,r\)) 分别设置一个limit
标记去解决。同时,因为要枚举两个数字,要分别为两个数字设上下标记,即一共要设\(4\)个标记。
#include <bits/stdc++.h>
#define TEST int T; read(T); while(T --)
//#define int long long
#define endl '\n'
using namespace std;
typedef long long LL;
template < typename T >
inline void read(T &x)
{
x = 0; bool f = 0; char ch = getchar();
while(!isdigit(ch)){f ^= !(ch ^ 45);ch=getchar();}
while(isdigit(ch)) x= (x<<1)+(x<<3)+(ch&15),ch=getchar();
x = f ? -x : x;
}
template<typename T,typename ...Args>void read(T &x, Args &...args)
{ read(x),read(args...); }
template <typename T> void read(vector<T> &A) { for(T &x: A) read(x);};
LL L[64], R[64], dp[64][2][2][2][2];
LL cnt, cnt1, cnt2;
inline void solve() {
LL a, b;
read(a, b);
// 题目大意 0-a 0-b 之间选择两个数字,求出 XOR 最大值
/**
* 利用数位DP
* 每次求出当前位可能的最大值
* 递归求出即可
* 较为模板化
*/
function <LL(int, bool, bool, bool, bool)> dfs = [&] (int pos, bool lit1, bool rit1, bool lit2, bool rit2) {
LL ans = 0;
if(pos == cnt) return 0LL;
auto &d = dp[pos][lit1][rit1][lit2][rit2];
if(d != -1)
return d;
for(LL u = (lit1 ? L[pos] : 0); u <= (rit1 ? R[pos] : 1); u ++ )
for(LL v = (lit2 ? L[pos] : 0); v <= (rit2 ? R[pos] : 1); v ++ )
ans = max(ans, ((u ^ v) << (cnt - pos - 1)) + dfs(pos + 1, lit1 && u == L[pos], rit1 && u == R[pos], lit2 && v == L[pos], rit2 && v == R[pos])) ;
return ans;
};
while(a) L[cnt1 ++] = a & 1, a >>= 1;
while(b) R[cnt2 ++] = b & 1, b >>= 1;
memset(dp, -1, sizeof dp);
cnt = max(cnt1, cnt2);
reverse(L, L + cnt);
reverse(R, R + cnt);
printf("%lld", dfs(0, true, true, true, true));
}
signed main()
{
solve();
return 0;
}
2020年ICPC区域赛上海站的C题也是二进制数位DP。
\(\sum_{i=0}^{X} \sum_{j=[i==0]}^{Y}\) [\(i\) & \(j\)\(==\)\(9\)] \(\left \lfloor {\log_{2}{i + j } + 1} \right \rfloor\) \(modulo\) \(10^9 + 7\)
对于给定的 \(X,Y\)求出上式的值
首先分析一下这个式子
如果有值,则\(i&j\)一定为\(0\),这就代表了\((i + j)\)是不涉及进位的,那么\(\left \lfloor {\log_{2}{i + j } + 1} \right \rfloor\)代表的值其实就是 \(\max(i, j)\)的最高位在哪一位.
因此我们就可以进行 数位DP 对每一位进行 记忆化搜索即可 qwq
#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define leng(a) (int)a.size()
#define lowbit(x) (x&-x)
#define fix(a) fixed << setprecision(a)
#define debug(x) cout<<#x" ----> "<<x<<endl
#define rep(i, b, s) for(int i = (b); i <= (s); ++i)
#define pre(i, b, s) for(int i = (b); i >= (s); --i)
#define TEST int T; read(T); while(T --)
//#define int long long
#define endl '\n'
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
#define all(v) (v).begin(),(v).end()
using namespace std;
typedef unsigned long long ULL;
typedef pair<int, int> PII ;
typedef pair<int, PII> PIII ;// {value, {value, value}}
typedef pair<double, double> PDD ;
typedef long long LL;
const int INF = INT_MAX;
const LL INFF = INT64_MAX;
const int MOD = 1e9 + 7;
const double eps = 1e-10;
const double pi = acos(-1.0);
LL gcd(LL a, LL b) {return b ? gcd(b, a%b) : a;}
inline LL ksm(LL a, LL b) {if (b == 0) return 1; LL ns = ksm(a, b >> 1); ns = ns * ns % MOD; if (b & 1) ns = ns * a % MOD; return ns;}
inline LL lcm(LL a, LL b) {return a / gcd(a, b) * b;}
inline void out(bool flag);
inline bool valid(char c) { return 33<=c&&c<=126; }
template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }
template <typename T> inline T mod(T &x) {return x % MOD;}
template < typename T >
inline void read(T &x)
{
x = 0; bool f = 0; char ch = getchar();
while(!isdigit(ch)){f ^= !(ch ^ 45);ch=getchar();}
while(isdigit(ch)) x= (x<<1)+(x<<3)+(ch&15),ch=getchar();
x = f ? -x : x;
}
template<typename T,typename ...Args>void read(T &x, Args &...args)
{ read(x),read(args...); }
template <typename T> void read(vector<T> &A) { for(T &x: A) read(x);};
inline void reads(char *s) {
int l=0;char c;
while(!valid(s[0]=getchar()));
while(valid(s[++l]=getchar()));
}
// __builtin_popcount(x) 返回x中1的个数
// __lg(x) 返回x的二进制下的位数(去除前导零实现,特判0)
//map<key,value>; key 可以是PII
//再用unordered_map 是sb
const int N = 64;
LL dp[N][2][2][2];
LL L[N], R[N];
int cnt, cnt1, cnt2;
inline void solve() {
LL a, b; read(a, b);
memset(dp, -1, sizeof dp);
memset(L, 0, sizeof L);
memset(R, 0, sizeof R);
cnt1 = 0, cnt2 = 0;
while(a) L[cnt1 ++] = a & 1, a >>= 1;
while(b) R[cnt2 ++] = b & 1, b >>= 1;
cnt = max(cnt1, cnt2);
reverse(L, L + cnt);
reverse(R, R + cnt);
function <LL(int, bool, bool, bool) > dfs = [&] (int pos, bool lim1, bool lim2, bool lead) {
LL ans = 0, res = 0;
if(pos == cnt) return 1LL;
auto &d = dp[pos][lim1][lim2][lead];
if(d != -1) return d;
for(int u = 0; u <= (lim1 ? L[pos] : 1); ++ u)
for(int v = 0; v <= (lim2 ? R[pos] : 1); ++ v) {
if(u & v) continue;
//111100
//012345
//6-
if(lead && (u || v)) res = cnt - pos;
else res = 1;
ans = (ans + dfs(pos + 1, lim1 && u == L[pos], lim2 && v == R[pos], lead && !u && !v) * res % MOD) % MOD;
}
d = ans;
return ans;
};
printf("%lld\n", (dfs(0, true, true, true) - 1 + MOD) % MOD);
}
signed main()
{
TEST
solve();
return 0;
}
inline void out(bool flag) {
if(flag) puts("YES");
else puts("NO");
}