专栏文章
题解:AT_abc425_g [ABC425G] Sum of Min of XOR
AT_abc425_g题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @minqjxrx
- 此快照首次捕获于
- 2025/12/02 06:43 3 个月前
- 此快照最后确认于
- 2025/12/02 06:43 3 个月前
G - Sum of Min of XOR
自己想的一个比较不同的做法。
好写好理解。 观察。 注意力。
题意
令 。
求 。
。
做法
若 比较小,则可以建 trie 树,遍历 到 ,从高位到低位,每次能往相同的走就尽量走。
不能的话则第 位会为答案增加一个 的贡献。
这启发我们去计算 trie 每一个树上的节点产生了多少贡献。
即大概是求一个节点会被多少个数走到,然后有多少个数会向 走。
但这似乎有点困难。
因为走到一个节点上的数是多个区间。
还有 的限制,这导致了一个节点向 走的数的个数还不一样。
想想看,如果 ,那么可以保证的是 trie 树上任何一个节点
向 走的数个数都是一样的。
在 dfs 的过程中维护当前位和走到当前节点的数的个数即可。
容易想到我们可以把 拆成若干个长度为 的区间来做。
具体来说,就是先找到一个极大的区间 ,然后继续去拆 。
对于一个 的区间,我们可以先在 trie 树上找到他们共同的父亲,然后再跑 dfs。
这样就等价于 的问题了。
CPPint n,m,a[N];
int son[N * 32][2],tot;
void insert(int x)
{
int p = 0;
for (int j = 30;j >= 0;j--)
{
int ch = (x >> j & 1);
if (!son[p][ch]) son[p][ch] = ++tot;
p = son[p][ch];
}
}
ll ans = 0;
void solve(int x,int w,int cnt)
{
if (!son[x][0] && !son[x][1]) return;
if (son[x][0] && son[x][1])
{
solve(son[x][0],w-1,cnt/2);
solve(son[x][1],w-1,cnt/2);
}
else if (son[x][0])
{
ans += ((1ll<<w) * cnt / 2);
solve(son[x][0],w-1,cnt);
}
else{
ans += ((1ll<<w) * cnt / 2);
solve(son[x][1],w-1,cnt);
}
}
void solvemain()
{
cin >> n >> m;
m--;
rep(i,1,n) cin >> a[i],insert(a[i]);
for (int i = 30;i >= 0;i--)
if (m >> i & 1)
{
int p = 0,cnt = (1<<i);
for (int j = 30;j >= i;j--)
{
int ch = (m >> j & 1);if(j==i)ch=0;
if (son[p][ch])p=son[p][ch];
else p = son[p][!ch],ans += cnt * (1ll<<j);
}
solve(p,i - 1,cnt);
}
int p = 0,cnt = 1; // 还剩下一个 [m,m]
for (int j = 30;j >= 0;j--)
{
int ch = (m >> j & 1);
if (son[p][ch])p=son[p][ch];
else p = son[p][!ch],ans += cnt * (1ll<<j);
}
cout << ans << endl;
}
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...