1001
签到,找到一个字符串第一次出现的位置,不讲
1002
一群人比赛,每次相邻的两个人决出一个胜者进入下一轮,直到只剩一个。现在要保证第iii个人是最后胜者,并且不遇上指定的k个人的概率。对于不含第iii个人的对决,双方获胜的概率相同。
这个过程是一个二叉树,我们可以直接树形dp,dpjdp_jdpj表示子树jjj最后剩下的那个人是iii不想与遇到的概率。
正常情况的转移就是dpj=(dp2j+dp2j+1)/2dp_j=(dp_{2j}+dp_{2j+1})/2dpj=(dp2j+dp2j+1)/2,因为两个儿子获胜的概率相等。如果两个子树,有一个是包含iii这个人的,那这个子树的胜者一定是iii,并且iii不想遇到另一个子树里对他有威胁的人,因此乘上另一个子树的胜者,不是对iii有威胁的概率,也就是(1−fj)(1-f_j)(1−fj)
但是n=1e9,k=1e5n=1e9,k=1e5n=1e9,k=1e5,直接建树会爆。用一下虚树的思想,我们只需要包含kkk个关键点和iii的树,并不需要对整个树建树吗,这样点数是O(klogn)O(klogn)O(klogn)的,可以接受。
实际上甚至都不用建树,可以直接用一个vectorvectorvector存所有点,每轮合并,并模拟往根走就行了。
这样做需要注意,我们用堆式存储推导父亲,需要从0开始存下标。否则1,2,3,41,2,3,41,2,3,4,父亲应该是1,1,2,21,1,2,21,1,2,2,但是直接除二会出现0,1,1,20,1,1,20,1,1,2
void solve() {
int n,k,id;
cin>>n>>k>>id;
id--;
vi a(k),dp(k,1);
rep(i,1,k){
cin>>a[i-1];
a[i-1]--;
}
sort(begin(a),end(a));
int ans=1;
int inv2=(M2+1)/2;
while(a.size()){
int sz=a.size();
id/=2;
vi na,ndp;
rep(i,0,sz-1){
if(a[i]/2==id){
ans=ans*(1-dp[i]+M2)%M2;
}
else{
na.push_back(a[i]/2);
if(i+1<sz&&a[i]/2==a[i+1]/2){
ndp.push_back((dp[i]+dp[i+1])*inv2%M2);
i++;
}
else if(a[i]+1<n){
ndp.push_back(dp[i]*inv2%M2);
}
else{
ndp.push_back(dp[i]);
}
}
}
swap(a,na);
swap(dp,ndp);
n=(n+1)/2;
}
cout<<ans<<'\n';
}
1003
给一个邻接矩阵,0/1表示有无边,p表示这个边有pp%p的概率出现,可以在这个图中选一些边生成一棵树,每次询问给一个ppp,问这个概率下,生成树的不同方案数的期望?
首先矩阵树定理可以计算生成树方案数,然后这里引入了边出现的概率,再用矩阵树定理求出来的就是所有方案的概率之和了。而这个东西实际上就等于不同方案数的期望,因为E(x)=∑p(xi)∗xiE(x)=\sum{p(x_i)*x_i}E(x)=∑p(xi)∗xi,而xi=1x_i=1xi=1。
这里也可以这样理解,矩阵树定理对于有边权的情况,求出来的是所有生成树的边权和,那我们把出现概率ppp的边,看成这条边的边权是ppp,所求出来的所有生成树的边权和,实际上就是方案数的期望
然后问题是有多次询问,肯定是预处理出来然后每次直接回答,不可能每次都现场算。注意到矩阵树定理其实是求行列式,而n∗nn*nn∗n矩阵的行列式实际上就是一个nnn次多项式,所以可以插值求出来这个多项式的系数。
插值需要nnn个数据点,每个数据点计算需要矩阵树定理求行列式,是O(n3)O(n^3)O(n3),最后插值复杂度是O(n2)O(n^2)O(n2),总复杂度O(n4)O(n^4)O(n4)
最后处理回答,每次都用nnn个系数算出答案,复杂度O(qn)O(qn)O(qn)
std::vector<int> lagrange_interpolation(const std::vector<int> &x,
const std::vector<int> &y,int MOD) {
const int n = x.size();
std::vector<int> M(n + 1), xx(n), f(n);
M[0] = 1;
// 求出 M(x) = prod_(i=0..n-1)(x - x_i)
for (int i = 0; i < n; ++i) {
for (int j = i; j >= 0; --j) {
M[j + 1] = (M[j] + M[j + 1]) % MOD;
M[j] = (LL)M[j] * (MOD - x[i]) % MOD;
}
}
// 求出 xx_i = M'(x_i) = (M / (x - x_i)) mod (x - x_i) 一定非零
for (int i = n - 1; i >= 0; --i) {
for (int j = 0; j < n; ++j) {
xx[j] = ((LL)xx[j] * x[j] + (LL)M[i + 1] * (i + 1)) % MOD;
}
}
// 组合出 f(x) = sum_(i=0..n-1)(y_i / M'(x_i))(M / (x - x_i))
for (int i = 0; i < n; ++i) {
LL t = (LL)y[i] * inv(xx[i],MOD) % MOD, k = M[n];
for (int j = n - 1; j >= 0; --j) {
f[j] = (f[j] + k * t) % MOD;
k = (M[j] + k * x[i]) % MOD;
}
}
return f;
}
class ModMatrix {
private:
std::vector<std::vector<long long>> data;
size_t rows;
size_t cols;
long long mod;
// 快速幂求逆元
long long power(long long base, long long exp) const {
long long result = 1;
base %= mod;
while (exp > 0) {
if (exp & 1) result = (result * base) % mod;
base = (base * base) % mod;
exp >>= 1;
}
return result;
}
// 模意义下的除法(乘法逆元)
long long modDivide(long long a, long long b) const {
return (a * power(b, mod - 2)) % mod;
}
public:
ModMatrix(size_t r, size_t c, long long m) : rows(r), cols(c), mod(m) {
data.resize(rows, std::vector<long long>(cols, 0));
}
size_t getRows() const { return rows; }
size_t getCols() const { return cols; }
long long& operator()(size_t i, size_t j) {
return data[i][j];
}
const long long& operator()(size_t i, size_t j) const {
return data[i][j];
}
// 模意义下的行列式计算(使用高斯消元)
long long determinant() const {
if (rows != cols) {
throw std::invalid_argument("Matrix must be square");
}
// 创建矩阵的副本
std::vector<std::vector<long long>> temp = data;
long long det = 1;
// 高斯消元
for (size_t i = 0; i < rows; i++) {
// 找主元
size_t pivot = i;
for (size_t j = i + 1; j < rows; j++) {
if (temp[j][i] != 0) {
pivot = j;
break;
}
}
if (temp[pivot][i] == 0) return 0; // 矩阵奇异
// 交换行
if (pivot != i) {
std::swap(temp[i], temp[pivot]);
det = (mod - det) % mod; // 行交换改变符号
}
// 消元
long long inv = power(temp[i][i], mod - 2); // 乘法逆元
det = (det * temp[i][i]) % mod;
for (size_t j = i + 1; j < rows; j++) {
long long factor = (temp[j][i] * inv) % mod;
if (factor == 0) continue;
for (size_t k = i; k < cols; k++) {
temp[j][k] = (temp[j][k] - (factor * temp[i][k]) % mod + mod) % mod;
}
}
}
return (det + mod) % mod;
}
};
struct Edge {
int from, to;
long long weight;
Edge(int f, int t, long long w = 1) : from(f), to(t), weight(w) {}
};
// 计算生成树权值和(模意义下)
long long matrixTreeTheoremMod(int n, const std::vector<Edge>& edges, long long mod) {
// 构建拉普拉斯矩阵
ModMatrix L(n, n, mod);
// 填充拉普拉斯矩阵
for (const Edge& edge : edges) {
int i = edge.from;
int j = edge.to;
long long w = edge.weight % mod;
// 对角线上加度数
L(i, i) = (L(i, i) + w) % mod;
L(j, j) = (L(j, j) + w) % mod;
// 非对角线位置填-w
L(i, j) = (L(i, j) + (mod - w)) % mod;
L(j, i) = (L(j, i) + (mod - w)) % mod;
}
// 去掉最后一行和最后一列
ModMatrix cofactor(n-1, n-1, mod);
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-1; j++) {
cofactor(i, j) = L(i, j);
}
}
// 返回模意义下的行列式值
return cofactor.determinant();
}
void solve() {
int n,q;
cin>>n>>q;
char c[n+1][n+1];
rep(i,1,n){
rep(j,1,n){
cin>>c[i][j];
}
}
vi x,y;
rep(k,1,n){
int p=inv(k,M1);
vector<Edge>e;
rep(i,1,n){
rep(j,i+1,n){
if(c[i][j]=='1')e.push_back(Edge(i-1,j-1,1));
else if(c[i][j]=='0')e.push_back(Edge(i-1,j-1,0));
else e.push_back(Edge(i-1,j-1,p));
}
}
int res=matrixTreeTheoremMod(n,e,M1);
x.push_back(p);
y.push_back(res);
}
vi f=lagrange_interpolation(x,y,M1);
// for(int x:f)cout<<x<<' ';
// cout<<'\n';
int ans=0;
rep(i,1,q){
int a,b;
cin>>a>>b;
int p=a*inv(b,M1)%M1;
int sum=0;
int pw=1;
rep(j,0,n-1){
sum=(sum+f[j]*pw%M1)%M1;
pw=pw*p%M1;
}
ans=(ans+i*sum%M1)%M1;
// cout<<sum<<'\n';
}
cout<<ans<<'\n';
}
1004
一个区间是海浪的定义是这样,
可以转化为:这个区间奇数位置最大值不超过偶数位置最小值,或者偶数位置最大值不超过奇数位置最小值。这个东西的判断我们可以开4个ststst表,分别存奇数/偶数位置的最大/最小值
现在有一堆区间询问,问每个区间里的最长海浪长度?
先考虑只有一个询问怎么做,首先显然一个区间越长,最大值肯定单增,最小值肯定单减,那么就越不容易成为海浪,也就是区间长度有单调性,那可以滑窗求出每个点i,i∈[1,n]i,i∈[1,n]i,i∈[1,n]为右端点时的最长海浪
然后询问一个[l,r][l,r][l,r]时,可以把这里的iii分成三种,记一个iii为右端点的最长海浪左端点为lil_ili,则可能成为答案的三种海浪为li<l,i<=r;li>=l,i<=r;li<=r,i>rl_i<l,i<=r;l_i>=l,i<=r;l_i<=r,i>rli<l,i<=r;li>=l,i<=r;li<=r,i>r
第一种可以二分最大的满足这个条件的iii,显然j∈[l,i]j∈[l,i]j∈[l,i]的所有点的海浪长度都是j−lj-lj−l,这里面最大的就是i−li-li−l。
然后j∈[i+1,n]j∈[i+1,n]j∈[i+1,n]都是第二类,我们求max(i−li)max(i-l_i)max(i−li)即可,这可以st表st表st表
最后第三类,注意到之前推出的单调性,如果存在一个li<=r,i>rl_i<=r,i>rli<=r,i>r,那么肯定也存在lj=li,j=rl_j=l_i,j=rlj=li,j=r,而这个jjj属于第二类,因此不用讨论第三类
1005
网格图上行走,经过一个点有耗时aija_{ij}aij,在(i,j)(i,j)(i,j)转向有额外耗时bijb_{ij}bij。问从左上角朝右进入,右下角朝下离开的最小耗时。
首先网格图最短路可以用一个BFSBFSBFS+dpdpdp数组记录最优解来解决。然后本题还有方向这个问题,那可以BFSBFSBFS加一个维度表示方向,注意dpdpdp数组也要增加一个维度。
也可以建图,相当于分层图。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
const int N=1e5+10;
void solve(){
int n,m;
cin>>n>>m;
vector<vector<int>>a(n,vector<int>(m));
vector<vector<int>>b(n,vector<int>(m));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>a[i][j];
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>b[i][j];
}
}
priority_queue<array<int,4>,vector<array<int,4>>,greater<array<int,4>>>q;
q.push({0,0,0,3});
int dx[4]={1,0,-1,0};
int dy[4]={0,-1,0,1};
vector<vector<vector<int>>>dp(n,vector<vector<int>>(m,vector<int>(4,1e18)));
int ans=1e18;
while(q.size()){
auto t=q.top();
q.pop();
int x=t[1],y=t[2],dis=t[0],dir=t[3];
if(dis>dp[x][y][dir])continue;
dp[x][y][dir]=dis;
for(int i=0;i<4;i++){
int xx=x+dx[i],yy=y+dy[i];
if(xx==n&&yy==m-1){
ans=min(ans,dis+a[x][y]+(dir==i?0:b[x][y]));
}
if(xx<0||xx>=n||yy<0||yy>=m)continue;
if(i==dir){
q.push({dis+a[x][y],xx,yy,i});
}
else{
q.push({dis+a[x][y]+b[x][y],xx,yy,i});
}
}
}
cout<<ans<<'\n';
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
}
1006
给一堆形如ax+b=cax+b=cax+b=c的方程,其中只知道a,b,ca,b,ca,b,c的值,但顺序不确定。问这些方程组成的方程组的正整数解?保证正整数解有且只有一个
顺序不确定就是说三个系数可以随意排列,由于只有三个,全排列也不多,可以直接暴力枚举,算出每个方程的所有正整数解。最后看出现nnn次的那个,就是答案
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
const int N=1e5+10;
const int mod=1e9+7;
void solve(){
int n;
cin>>n;
map<int,int>mp;
for(int i=0;i<n;i++){
set<int>s;
auto &&cal=[&](int x,int y,int z)->void{
if((x-y)%z==0){
s.insert((x-y)/z);
}
};
int a,b,c;
cin>>a>>b>>c;
cal(a,b,c);
cal(a,c,b);
cal(b,a,c);
cal(b,c,a);
cal(c,a,b);
cal(c,b,a);
for(int x:s){
if(x>=0){
mp[x]++;
}
}
}
for(auto &[k,v]:mp){
if(v==n){
cout<<k<<'\n';
}
}
}
signed main(){
std::ios::sync_with_stdio(0);
std::cin.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
}
1007
三姬分金强化版。n个人,排名第一的提出分配方案,然后投票,如果有半数同意就通过,否则杀死排名第一的,让排名第二的继续这个过程,问第一个人,最少需要给其他每个人多少钱,才能在保证自己活着的前提下,尽可能少给其他人钱?
最后的结论是,从第二个人开始,应该形如0101..0 1 0 1..0101..这样给,不会证
1008
一个凸多边形轮子,滚动过程中要有一点到地面距离不变,要给他定做一个轨道,并且让轨道和地面围成的面积尽可能小。那实际上我们求出来最小圆覆盖,然后把这个圆里面挖掉凸多边形,摊开就是面积最小的轨道了。
所以求一下最小圆覆盖和凸包面积,做差就行了。然后这题要求的是轨道面积和轨道长度的比值,在长度区域无穷时的极限,再除以最小圆覆盖周长即可,因为这个轨道是以周长为周期的
最小圆覆盖用随机增量法,先打乱所有点,然后逐个增加,复杂度O(n)O(n)O(n)。凸包复杂度O(nlogn)O(nlogn)O(nlogn),求凸包面积可以用相邻点的叉乘之和除二,取绝对值,这是O(n)O(n)O(n)的
不知道哪被卡常了,在杭电oj上一直不过,但应该是对的
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define LL ll
#define int ll
#define db double
#define ld long double
#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define rep1(i,x,y) for(int i=(x);i>=(y);i--)
#define pii pair<int,int>
#define pll pair<ll,ll>
#define vi vector<int>
#define vvi vector<vector<int>>
#define pb push_back
#define fi first
#define se second
using namespace std;
int n;
double r;
struct point {
double x, y;
} p[100005], o;
double sqr(double x) { return x * x; }
double dis(point a, point b) { return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y)); }
bool cmp(double a, double b) { return fabs(a - b) < 1e-8; }
point geto(point a, point b, point c) {
double a1, a2, b1, b2, c1, c2;
point ans;
a1 = 2 * (b.x - a.x), b1 = 2 * (b.y - a.y),
c1 = sqr(b.x) - sqr(a.x) + sqr(b.y) - sqr(a.y);
a2 = 2 * (c.x - a.x), b2 = 2 * (c.y - a.y),
c2 = sqr(c.x) - sqr(a.x) + sqr(c.y) - sqr(a.y);
if (cmp(a1, 0)) {
ans.y = c1 / b1;
ans.x = (c2 - ans.y * b2) / a2;
} else if (cmp(b1, 0)) {
ans.x = c1 / a1;
ans.y = (c2 - ans.x * a2) / b2;
} else {
ans.x = (c2 * b1 - c1 * b2) / (a2 * b1 - a1 * b2);
ans.y = (c2 * a1 - c1 * a2) / (b2 * a1 - b1 * a2);
}
return ans;
}
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%lf%lf", &p[i].x, &p[i].y);
for (int i = 1; i <= n; i++) swap(p[rand() % n + 1], p[rand() % n + 1]);
o = p[1];
for (int i = 1; i <= n; i++) {
if (dis(o, p[i]) < r || cmp(dis(o, p[i]), r)) continue;
o.x = (p[i].x + p[1].x) / 2;
o.y = (p[i].y + p[1].y) / 2;
r = dis(p[i], p[1]) / 2;
for (int j = 2; j < i; j++) {
if (dis(o, p[j]) < r || cmp(dis(o, p[j]), r)) continue;
o.x = (p[i].x + p[j].x) / 2;
o.y = (p[i].y + p[j].y) / 2;
r = dis(p[i], p[j]) / 2;
for (int k = 1; k < j; k++) {
if (dis(o, p[k]) < r || cmp(dis(o, p[k]), r)) continue;
o = geto(p[i], p[j], p[k]);
r = dis(o, p[i]);
}
}
}
vector<array<db,2>>a;
rep(i,1,n){
a.push_back({p[i].x,p[i].y});
}
sort(a.begin(),a.end(),[&](array<db,2>&x,array<db,2>&y){
if(x[0]==y[0])return x[1]<y[1];
return x[0]<y[0];
});
stack<int>s;
auto cross=[&](db x1,db y1,db x2,db y2)->db{
return x1*y2-x2*y1;
};
auto check=[&](int x)->bool{
auto t1=s.top();
s.pop();
auto t2=s.top();
s.push(t1);
return cross(a[t1][0]-a[t2][0],a[t1][1]-a[t2][1],a[x][0]-a[t1][0],a[x][1]-a[t1][1])<0;
};
rep(i,0,n-1){
while(s.size()>=2&&check(i)){
s.pop();
}
s.push(i);
}
vi res,res1;
while(s.size()){
res.push_back(s.top());
s.pop();
}
reverse(res.begin(),res.end());
rep1(i,n-1,0){
while(s.size()>=2&&check(i)){
int x=s.top();
s.pop();
}
s.push(i);
}
while(s.size()){
res1.push_back(s.top());
s.pop();
}
reverse(res1.begin(),res1.end());
for(int x:res1){
res.push_back(x);
}
db pi=acos(-1);
db a1=pi*r*r;
db a2=0;
int m=res.size();
rep(i,1,m-1){
int j=res[i]+1,k=res[i-1]+1;
a2+=cross(p[j].x,p[j].y,p[k].x,p[k].y);
}
// cout<<a1<<' '<<a2<<' ';
a1-=abs(a2/2);
db len=2*pi*r;
printf("%.6lf\n",a1/len);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int t=1;
cin>>t;
while(t--){
solve();
}
}
1009
划分型dp每一段的价值是f(l,r)f(l,r)f(l,r),f(l,r)f(l,r)f(l,r)的第iii位,只有在al...ara_l...a_ral...ar的第iii位都不全为0,也不全为1时才是1,否则为0.
这个条件写一下真值表,可以发现就是or−andor-andor−and。然后根据logtricklogtricklogtrick可知,以rrr为右端点的所有区间里,不同的f(l,r)f(l,r)f(l,r)只有logV=mlogV=mlogV=m种,也就是说我们可以把区间分成mmm段,每段里f(l,r)f(l,r)f(l,r)都相同,我们只用和每一段的最大值进行转移即可
找到这mmm段,可以用ststst表+二分解决,每次二分除来满足or−andor-andor−and不变的最小lll。然后进行一个区间查,查这一段的dpdpdp最大值,进行转移。
当然也可以维护一个数组,保存每一段的最大值,每次增加一个元素aja_jaj,每一段都相当于变成(li,ri)−>(li,ri+1)(l_i,r_i)->(l_i,r_i+1)(li,ri)−>(li,ri+1)。可能有两段的or−andor-andor−and再加入aja_jaj后相同了,把他们合并。这样复杂度少一个logloglog,但是合并有点麻烦。
class SparseTable {
public:
SparseTable(const std::vector<int>& data) {
n = data.size();
log.resize(n + 1);
for (int i = 2; i <= n; ++i) {
log[i] = log[i / 2] + 1;
}
int k = log[n];
st.assign(n, std::vector<int>(k + 1, 0));
for (int i = 0; i < n; ++i) {
st[i][0] = data[i];
}
for (int j = 1; j <= k; ++j) {
for (int i = 0; i + (1 << j) <= n; ++i) {
st[i][j] = st[i][j - 1] & st[i + (1 << (j - 1))][j - 1];
}
}
}
int query(int left, int right) {
int j = log[right - left + 1];
return st[left][j] & st[right - (1 << j) + 1][j];
}
private:
int n; // 数据的大小
std::vector<std::vector<int>> st; // 稀疏表
std::vector<int> log; // 记录对数值
};
class SparseTable1 {
public:
SparseTable1(const std::vector<int>& data) {
n = data.size();
log.resize(n + 1);
for (int i = 2; i <= n; ++i) {
log[i] = log[i / 2] + 1;
}
int k = log[n];
st.assign(n, std::vector<int>(k + 1, 0));
for (int i = 0; i < n; ++i) {
st[i][0] = data[i];
}
for (int j = 1; j <= k; ++j) {
for (int i = 0; i + (1 << j) <= n; ++i) {
st[i][j] = st[i][j - 1] | st[i + (1 << (j - 1))][j - 1];
}
}
}
int query(int left, int right) {
int j = log[right - left + 1];
return st[left][j] | st[right - (1 << j) + 1][j];
}
private:
int n; // 数据的大小
std::vector<std::vector<int>> st; // 稀疏表
std::vector<int> log; // 记录对数值
};
struct Tree{
struct Node{
int l,r;
ll mx,add;
}tr[N<<2];
#define ls u<<1
#define rs u<<1|1
void pushup(int u){
tr[u].mx=max(tr[ls].mx,tr[rs].mx);
}
void pushdown(int u){
if(tr[u].add){
tr[ls].mx=tr[u].add;
tr[rs].mx=tr[u].add;
tr[ls].add=tr[u].add;
tr[rs].add=tr[u].add;
tr[u].add=0;
}
}
void build(int u,int l,int r){
tr[u]={l,r,-P,0};
if(l==r){
return;
}
int mid=(l+r)>>1;
build(ls,l,mid); build(rs,mid+1,r);
pushup(u);
}
void modify(int u,int l,int r,int val){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].mx=val;
tr[u].add=val;
return ;
}
else{
int mid=(tr[u].l+tr[u].r)>>1;
pushdown(u);
if(mid>=l) modify(ls,l,r,val);
if(r>mid) modify(rs,l,r,val);
pushup(u);
}
}
ll query(int u,int l,int r){
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].mx;
pushdown(u);
int mid=(tr[u].l+tr[u].r)>>1;
if(r<=mid)return query(ls,l,r);
if(l>mid)return query(rs,l,r);
return max(query(ls,l,r),query(rs,l, r));
}
}t;
void solve() {
int n,m;
cin>>n>>m;
t.build(1,1,n);
t.modify(1,0,0,0);
vi a(n+1),g(1<<m);
rep(i,1,n){
cin>>a[i];
}
rep(i,1,1<<m){
cin>>g[i-1];
}
SparseTable st1(a);
SparseTable1 st2(a);
vi dp(n+1);
rep(i,1,n){
int pos=i;
while(pos>=1){
// cout<<pos<<' ';
int cur=st2.query(pos,i)-st1.query(pos,i);
int l=1,r=pos;
while(l<=r){
int m=l+r>>1;
if(st2.query(m,i)-st1.query(m,i)==cur){
r=m-1;
}
else{
l=m+1;
}
}
// cout<<l<<' '<<pos<<' '<<t.query(1,l-1,pos-1)+g[cur]<<'\n';
dp[i]=max(dp[i],t.query(1,l-1,pos-1)+g[cur]);
pos=l-1;
}
t.modify(1,i,i,dp[i]);
// cout<<dp[i]<<'\n';
// cout<<'\n';
}
cout<<dp[n]<<'\n';
}