看到異或直接想到線性基和trie
很明顯是trie
從高到低一位位考慮,如果兩個兒子都有,想使這一位為1,必須有一個變成加法
然後就便利一下trie,記錄一下剩餘的體力和最小的加法的數就好了
#include<bits/stdc++.h>
using namespace std;
#define ll __int128
#define ls tr[u][0]
#define rs tr[u][1]
ll read(){
char ch=getchar();ll x=0;
while(ch<'0' || ch>'9')ch=getchar();
while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch-'0'),ch=getchar();
return x;
}
void write(ll x){
if(x>=10)write(x/10);
putchar(x%10+'0');
}
int T,C,n,m,k,b[100005],tr[12000005][2],cnt;
ll res,a[100005],sumb[12000005],minb[12000005];
void init(){
res=0;
for(int i=1;i<=cnt;i++)tr[i][0]=tr[i][1]=sumb[i]=0,minb[i]=(ll)1<<120;
cnt=1;
}
void insert(ll x,int y){
int u=1;
sumb[u]+=y;minb[u]=min(minb[u],x);
for(int i=k-1;i>=0;i--){
int p=(x>>i)&1;
if(!tr[u][p])tr[u][p]=++cnt;
u=tr[u][p];sumb[u]+=y;minb[u]=min(minb[u],x);
}
}
void work(int u,int id,int la,ll s,ll x,ll miny){
if(id==-1){
res=max(res,s);
return ;
}
ll Mk=(ll)1<<id,mk=Mk-1;
if(!u){
res=max(res,miny+(x|Mk|mk));
return ;
}
bool flag=1;
if(sumb[ls]<=la && (x|mk)+min(miny,minb[ls])>=(s|Mk))
work(rs,id-1,la-sumb[ls],s|Mk,x,min(miny,minb[ls])),flag=0;
if(sumb[rs]<=la && (x|Mk|mk)+min(miny,minb[rs])>=(s|Mk))
work(ls,id-1,la-sumb[rs],s|Mk,x|Mk,min(miny,minb[rs])),flag=0;
if(flag){
work(ls,id-1,la,s,x,miny);
work(rs,id-1,la,s,x|Mk,miny);
}
return ;
}
int main(){
scanf("%d%d",&C,&T);
memset(minb,0x7f,sizeof(minb));
while(T--){
scanf("%d%d%d",&n,&m,&k);
init();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
for(int i=1;i<=n;i++)insert(a[i],b[i]);
// for(int i=1;i<=cnt;i++)printf("(%d %d)\n",tr[i][0],tr[i][1]);
// for(int i=1;i<=cnt;i++)write(sumb[i]),putchar(' ');puts("");
// for(int i=1;i<=cnt;i++)write(minb[i]),putchar(' ');puts("");
if(sumb[1]<=m){
write(minb[1]+((ll)1<<k)-1);putchar('\n');
continue;
}
work(1,k-1,m,0,0,(ll)1<<120);
write(res);putchar('\n');
}
return 0;
}
本來想到了,但是以為那個work的列舉是 \(O(2^k)\) ,卡了半天(降智了)