25杭电春季第一场

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)(1fj)

但是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*nnn矩阵的行列式实际上就是一个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-ljl,这里面最大的就是i−li-lil

然后j∈[i+1,n]j∈[i+1,n]j[i+1,n]都是第二类,我们求max(i−li)max(i-l_i)max(ili)即可,这可以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-andorand。然后根据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-andorand不变的最小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-andorand再加入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';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值