给定 和 ,有 个 的非负整数变量 ,其中 的取值范围是 。
给出数列 ,并由此定义一个代价函数 :
确定每个变量的取值,使得 最小,输出该最小值。
,,,。
考虑二进制意义下从高往低一位一位填,若当前填到了第 位且 之后的位无论怎么填都不会超出 的限制,则 后面的位可以贪心地和 一样,所以 可以删掉。
不妨能删就删,即钦定 和 的高 位都顶着上界和下界中的至少一个。
那么有一个朴素的 dp,设 表示填完高 位,删掉了区间 , 顶到上界/下界, 顶到上界/下界。
发现这样转移不了,因为若 在第 位时解除了限制,则它的第 位需要翻转。不妨把这个东西加入状态,设 表示填完高 位,删掉了区间 ; 顶到上界/下界,是否在第 位解除限制; 顶到上界/下界,是否在第 位解除限制。
注意这样设计状态后 才是我们想要的东西, 和 只是中间状态。
转移较为简单,注意到 只有两个来源,即 和 。前者对应 没有在第 位解除限制的变量,后者则对应有这样的变量。
注意 时后面没有位了,要特判。
时间复杂度 ,使用记忆化搜索实现会简洁很多,代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int S=55;
const long long inf=1e18;
int n,m;
long long L[S],R[S],c[S];
long long f[S][S][S][2][2][2][2];
inline void tom(long long &x,long long y){x=min(x,y);}
long long g(int p,int l,int r,int l0,int l1,int r0,int r1)
{
if(p==m) return l>r?0:inf;
long long &u=f[p][l][r][l0][l1][r0][r1];
if(u!=-1) return u;
int stl=(((l0==0?L[l-1]:R[l-1])>>p)&1)^l1;
int str=(((r0==0?L[r+1]:R[r+1])>>p)&1)^r1;
u=inf;
tom(u,g(p+1,l,r,l0,0,r0,0)+(l!=1&&r!=n&&stl!=str)*c[p]);
for(int k=l;k<=r;k++)
{
for(int t=0;t<=1;t++)
{
if(p==0) tom(u,g(p,l,k-1,l0,l1,t,0)+g(p,k+1,r,t,0,r0,r1));
long long vk=(t==0?L[k]:R[k])^(1ll<<p);
if((vk&(~((1ll<<p)-1)))>=L[k]&&(vk|((1ll<<p)-1))<=R[k])
{
tom(u,g(p,l,k-1,l0,l1,t,1)+g(p,k+1,r,t,1,r0,r1));
}
}
}
return u;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld%lld",&L[i],&R[i]);
for(int i=0;i<=m-1;i++) scanf("%lld",&c[i]);
memset(f,-1,sizeof(f));
printf("%lld\n",g(0,1,n,0,0,0,0));
return 0;
}