P10218 [省选联考 2024] 魔法手杖 题解

题目描述

给定序列 \(a_1,\cdots,a_n\)\(b_1,\cdots,b_n\) ,满足 \(a_i\in [0,2^k-1],b_i\ge 0\),你需要给出 \(S\subseteq \{1,\cdots,n\}\)\(x\in [0,2^k-1]\) 满足:

  • \(\sum\limits_{i\in S}b_i\le m\)
  • 最大化 \(val(S,x)=\min\big(\min\limits_{i\in S}(a_i+x),\min\limits_{i\not\in S}(a_i\oplus x)\big)\)

\(val(S,x)\) 的最大值。


数据范围

  • \(1\le n\le 10^5,1\le\sum n\le 5\cdot 10^5\)
  • \(0\le m,b_i\le 10^9\)
  • \(0\le k\le 120\)

时间限制 \(\texttt{1.5s}\),空间限制 \(\texttt{1024MB}\)

分析

特判掉 \(\sum_{i=1}^nb_i\le m\) 的情况,此时 \(ans=\min\limits_{1\le i\le n}a_i+2^k-1\)

看到异或直接往 \(\texttt{trie}\) 树上想,考虑从高到低确定 \(x\)\(ans\) 的每一位。

\(\texttt{trie}\) 树上递归时,我们已经考虑当前节点子树外的所有状态,并且正在考虑当前节点深度 \(d\) 的决策

如果 \(ans\)\(d\) 位取 \(1\) ,那么左右子树中至少有一棵为空或者全部放入 \(S\)

如果 \(ans\)\(d\) 位取 \(0\),那么\(x\) 当前位取值相反的子树一定取不到最小值,直接不予考虑。

递归时枚举 \(x\) 当前位取 \(0\) 还是 \(1\) 即可。

时间复杂度 \(\mathcal O(\sum nk)\)

#include<bits/stdc++.h>
#define ll __int128
using namespace std;
const int maxn=1e5+5,maxm=1.2e7+5;
int k,m,n,tot;
int ch[maxm][2];
ll res,a[maxn],b[maxn],pw[125],v1[maxm],v2[maxm];
ll read()
{
    ll q=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) q=10*q+ch-'0',ch=getchar();
    return q;
}
void write(ll x)
{
    if(x>=10) write(x/10);
    putchar(x%10+'0');
}
void dfs(int p,int d,ll amin,ll bsum,ll x,ll ans)
{
    if(!~d) return res=max(res,ans),void();
    if(!p) return res=max(res,amin+x+pw[d+1]-1),void();
    int flg=0,lc=ch[p][0],rc=ch[p][1];
    ///x=0,ans=1
    if(bsum+v2[lc]<=m&&min(amin,v1[lc])+x+pw[d]-1>=ans+pw[d])
        flg=1,dfs(rc,d-1,min(amin,v1[lc]),bsum+v2[lc],x,ans+pw[d]);
    ///x=1,ans=1
    if(bsum+v2[rc]<=m&&min(amin,v1[rc])+x+pw[d+1]-1>=ans+pw[d])
        flg=1,dfs(lc,d-1,min(amin,v1[rc]),bsum+v2[rc],x+pw[d],ans+pw[d]);
    if(flg) return ;
    ///x=0,ans=0
    dfs(lc,d-1,amin,bsum,x,ans);
    ///x=1,ans=0
    dfs(rc,d-1,amin,bsum,x+pw[d],ans);
}
int main()
{
    for(int i=0;i<125;i++) pw[i]=i?pw[i-1]<<1:1;
    v1[0]=pw[120];
    for(int c=read(),t=read();t--;)
    {
        n=read(),m=read(),k=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int i=1;i<=n;i++) b[i]=read();
        memset(ch+1,0,8*tot),memset(v2+1,0,16*tot),v1[tot=1]=pw[k];
        for(int i=1;i<=n;i++)
            for(int j=k-1,p=1;;j--)
            {
                v1[p]=min(v1[p],a[i]),v2[p]+=b[i];
                if(!~j) break;
                int v=a[i]>>j&1;
                if(!ch[p][v]) v1[ch[p][v]=++tot]=pw[k];
                p=ch[p][v];
            }
        if(v2[1]<=m) res=v1[1]+pw[k]-1;
        else res=0,dfs(1,k-1,pw[k],0,0,res);
        write(res),putchar('\n');
    }
    return 0;
}

总结:

  • \(\texttt{trie}\) 树有关的最优化问题一般会用到贪心的思想,笔者独立思考的时候跟着特殊性质 \(\texttt{A,B}\) 走到 \(\texttt{dp}\) 的坑里就爬不出来了。
posted @ 2024-07-03 19:25  peiwenjun  阅读(21)  评论(0编辑  收藏  举报