題目描述:
給定序列 \(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}\) 的坑裡就爬不出來了。