最简单的暴力dp就是f[i][j]表示到i异或和为j的最小花费。
然后我们发现两堆大小为i,j的石子合并,可以更新到一堆大小为k=i,j最高公共的1以下都是1,以上是i|j,权值为v1+v2的石子。
我们可以打表发现这个段数其实很小,其实也可以严谨的证明或者感性理解,但是我不会。。
所以我们记录当前的所有f值以及端点然后暴力转移就可以了。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #include <map> 7 #define N 100500 8 #define int long long 9 using namespace std; 10 int n,m,q,cnt1,cnt2,ans; 11 struct data{ 12 int v,l; 13 data(){} 14 data(int x,int y){v=x;l=y;} 15 bool operator < (const data & a)const{ 16 if(v==a.v)return l>a.l; 17 return v<a.v; 18 } 19 data operator + (data a){ 20 data b=data(v+a.v,l|a.l); 21 for(int i=30;~i;i--)if(l&a.l&(1<<i)) 22 {b.l|=(1<<i)-1;break;} 23 return b; 24 } 25 }f[N<<1],d; 26 signed main(){ 27 //freopen("test.in","r",stdin); 28 scanf("%lld%lld",&n,&m); 29 cnt1++; 30 f[1]=data(0,0); 31 register int i,j; 32 for(i=1;i<=n;i++){ 33 scanf("%lld%lld",&d.v,&d.l); 34 for(j=1;j<=cnt1;j++)f[cnt1+j]=f[j]+d; 35 sort(f+1,f+2*cnt1+1); 36 for(cnt2=1,j=2;j<=2*cnt1;j++) 37 if(f[j].l>f[cnt2].l)f[++cnt2]=f[j]; 38 cnt1=cnt2; 39 } 40 scanf("%lld",&q); 41 int x; 42 while(q--){ 43 scanf("%lld",&x); 44 ans=-1; 45 for(int i=1;i<=cnt1;i++)if(f[i].l>=x) 46 {ans=f[i].v;break;} 47 printf("%lld ",ans); 48 } 49 return 0; 50 }