1:A + B Problem agin
数字A + 数字B不进位,对于每个数字求相加最大的结果。
字典树的异或的板子题是不进位求a[i] ^ a[j] 最大,这题也类似。只是多了一个删除数字的操作,因为要避免同一个数字加两次。
关于删除,只需要在插入的时候有计数的数组val[p]++,意味着当前这个节点有一个字符,删除的时候,val[p] += add,add为1或-1,就能实现这个删除之后又更新的操作了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5, mod = 998244353;
int tree[N * 10][10], a[N], val[N * 10];
int tot = 1;
void insert(int x)
{
string s = to_string(x);
while(s.length() < 5) s = "0" + s;
int p = 1;
for (auto i : s) {
int num = i - '0';
if (tree[p][num] == 0)
tree[p][num] = ++tot;
p = tree[p][num];
val[p]++;
}
}
int search(int x)
{
string s = to_string(x);
while(s.length() < 5) s = "0" + s;
int p = 1;
int ret = 0;
for (auto i : s) {
int num = i - '0';
int ma = -1;
for (int j = 9; j >= 0; j--) {
if (tree[p][j] && val[tree[p][j]]) ma = max(ma, (num + j) % 10);
}
p = tree[p][((ma - num) % 10 + 10) % 10];
ret = ret * 10 + ma;
}
return ret;
}
void up(int x, int add)
{
string s = to_string(x);
while(s.length() < 5) s = "0" + s;
int p = 1;
for (auto i : s) {
int num = i - '0';
p = tree[p][num];
val[p] += add;
}
}
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
insert(a[i]);
}
for (int i = 1; i <= n; i++) {
up(a[i], -1);
printf("%lld ", search(a[i]));
up(a[i], 1);
}
}
signed main()
{
int tt = 1;
// cin >> tt;
while (tt--) solve();
return 0;
}
HDU5336:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000 + 5, mod = 998244353;
int tree[N * 32][10], a[N], val[N * 32];
int tot = 1;
void insert(int x)
{
int p = 1;
for (int i = 31; i >= 0; i--) {
int f = (x >> i) & 1;
if (tree[p][f] == 0) tree[p][f] = ++tot;
p = tree[p][f];
val[p]++;
}
}
int search(int x)
{
int p = 1;
int ret = 0;
for (int i = 31; i >= 0; i--){
int f = (x >> i) & 1;
if (tree[f ^ 1] && val[tree[p][f ^ 1]]) {
p = tree[p][f ^ 1];
ret |= (1ll << i);
}
else p = tree[p][f];
}
return ret;
}
void up(int x, int add)
{
int p = 1;
for (int i = 31; i >= 0; i--) {
int f = (x >> i) & 1;
p = tree[p][f];
val[p] += add;
}
}
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
insert(a[i]);
}
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++)
{
up(a[i], -1);
up(a[j], -1);
ans = max(ans, /* (a[i] + a[j]) ^ */search(a[i] + a[j]));
up(a[i], 1);
up(a[j], 1);
}
}
cout << ans << "\n";
for (int i = 1; i <= tot; i++) {
tree[i][0] = tree[i][1] = 0;
val[i] = 0;
}
tot = 1;
}
signed main()
{
int tt = 1;
cin >> tt;
while (tt--) solve();
return 0;
}
D2. 388535 (Hard Version)
给定一个区间LR之间的数异或x的序列,求x是多少。
做法:
枚举x,求x与这个序列的最大异或值和最小异或值,如果max == r && min == l,那么x是符合条件的。怎么枚举呢?可以枚举 a[i] ^ L,这样总能碰到那个x。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = (1<<17) + 5, mod = 998244353;
int tree[N * 17][10], a[N], val[N * 17];
int tot = 1;
void insert(int x)
{
int p = 1;
for (int i = 31; i >= 0; i--) {
int f = (x >> i) & 1;
if (tree[p][f] == 0) tree[p][f] = ++tot;
p = tree[p][f];
val[p]++;
}
}
int search_max(int x)
{
int p = 1;
int ret = 0;
for (int i = 31; i >= 0; i--){
int f = (x >> i) & 1;
if (tree[f ^ 1] && val[tree[p][f ^ 1]]) {
p = tree[p][f ^ 1];
ret |= (1ll << i);
}
else p = tree[p][f];
}
return ret;
}
int search_min(int x)
{
int p = 1;
int ret = 0;
for (int i = 31; i >= 0; i--){
int f = (x >> i) & 1;
if (tree[f] && val[tree[p][f]]) {
p = tree[p][f];
}
else {
p = tree[p][f ^ 1];
ret |= (1ll << i);
}
}
return ret;
}
void up(int x, int add)
{
int p = 1;
for (int i = 31; i >= 0; i--) {
int f = (x >> i) & 1;
p = tree[p][f];
val[p] += add;
}
}
void solve()
{
int l, r;
cin >> l >> r;
int n = r - l + 1;
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
insert(a[i]);
}
for (int i = 1; i <= n; i++) {
int ma = search_max(a[i] ^ l);
int mi = search_min(a[i] ^ l);
// printf("mi = %lld, ma = %lld\n", mi, ma);
if (mi == l && ma == r) {
cout << (a[i] ^ l) << "\n";
break;
}
}
for (int i = 1; i <= tot; i++) {
tree[i][0] = tree[i][1] = 0;
val[i] = 0;
}
tot = 1;
}
signed main()
{
int tt = 1;
cin >> tt;
while (tt--) solve();
return 0;
}