首页 > 其他分享 >法阵

法阵

时间:2024-10-15 22:02:30浏览次数:1  
标签:lg 法阵 max ll tree mid int

法阵

题意

给你一个长 \(n\) 的数列,有 \(t\) 次询问,每次询问区间 \([l,r]\) 里满足 \(l\le x<y<z \le r,y-x\le z-y\) 的三元组 \(a_x+a_y+a_z\) 的最大值。其中 \(n,t\leq 5\times 10^5\)。

solution

结论:最终对答案有贡献的二元组 \((x,y)\) 一定满足
\(a_x> \max_{i=x+1}^{y-1}(a_i),a_y> \max_{i=x+1}^{y-1}(a_i)\)。

证明:若存在 \(x<b<y,a_b>a_x \text{或} a_b>a_y\),那么选择二元组 \((x,b)\) 或 \((b,y)\) 一定更优。

这样合法的二元组一共有 \(O(n)\) 个。

我们使用单调队列找出它们。

从左往右加入 \(a_i\),维护单调递减的队列。因为一个数仍在单调队列中就说明它后面暂时没有比它大的元素,那么新增合法二元组 \((\text{踢出的元素},i)\) 和 \((\text{剩下的元素},i)\)。

找到合法二元组后,对于每一个二元组,我们都可以 ST 表或树状数组或线段树 \(O(\log n)\) 找出最优的 \(z\)。

这样就可以解决仅询问区间 \([1,n]\) 的问题。

但是我们有 \(t\) 次询问!考虑将询问离线下来,按左端点从大到小排序,在不考虑右端点的情况下,此时我们加入一些可能用上的二元组。记 \(ans_i\) 表示以 \(z=i\) 的目前的答案,\(ans_i= \max (\text{已加入的合法二元组})+a_i\)。用线段树维护 \(ans\)。我们加入一个二元组就相当于给 \(ans\) 的一个后缀都进行一次 \(\max\) 的操作,可以线段树区间修改,每次询问即在线段树上查找区间 \([l+2,r]\)。

总时间复杂度 \(O(n\log n)\)。

code

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define il inline
//#define int ll
using namespace std;
typedef long long ll;
const int N=5e5+7,mod=998244353;
int n,t;
ll a[N];
struct Q{ int id,l,r; }q[N];
bool cmp(Q a,Q b){ return a.l>b.l; }
vector<int> vec[N];
ll st[N];
int top;
ll ans[N];
ll tree[N<<2];
ll tag[N<<2];
ll stb[30][N];
int lg[N];
void pushdown(int u,int l,int r){
	int mid=(l+r)>>1;
	if(tag[u]>tag[u*2]){
		tree[u*2]=max(tree[u*2],tag[u]+max(stb[lg[mid-l+1]][l],stb[lg[mid-l+1]][mid-(1<<lg[mid-l+1])+1]));
		tag[u*2]=max(tag[u*2],tag[u]);
	}
	if(tag[u]>tag[u*2+1]){
		tree[u*2+1]=max(tree[u*2+1],tag[u]+max(stb[lg[r-(mid+1)+1]][mid+1],stb[lg[r-(mid+1)+1]][r-(1<<lg[r-(mid+1)+1])+1]));
		tag[u*2+1]=max(tag[u*2+1],tag[u]);
	}
}
void pushup(int u){
	tree[u]=max(tree[u*2],tree[u*2+1]);
}
void insert(int u,int l,int r,ll w,int x) {
	if(l>=x){
		tree[u]=max(tree[u],w+max(stb[lg[r-l+1]][l],stb[lg[r-l+1]][r-(1<<lg[r-l+1])+1]));
		tag[u]=max(tag[u],w);
		return;
	}
	int mid=(l+r)>>1;
	pushdown(u,l,r);
	if(mid>=x) insert(u*2,l,mid,w,x);
	insert(u*2+1,mid+1,r,w,x);
	pushup(u);
}
void build(int u,int l,int r){
	if(l==r) {
		if(l>=3)
		tree[u]=a[l]+a[l-1]+a[l-2];
		return;
	}
	int mid=(l+r)>>1;
	build(u*2,l,mid);
	build(u*2+1,mid+1,r);
	pushup(u);
}
ll ask(int u,int l,int r,int L,int R){
	if(l>=L&&r<=R){
		return tree[u];
	}
	int mid=(l+r)>>1;
	pushdown(u,l,r);
	ll mx=0;
	if(mid>=L) mx=max(mx,ask(u*2,l,mid,L,R));
	if(mid+1<=R) mx=max(mx,ask(u*2+1,mid+1,r,L,R));
	pushup(u);
	return mx;
}
int main(){
//	freopen("ex_fz1.in","r",stdin);
	freopen("fz.in","r",stdin);
	freopen("fz.out","w",stdout);
	sf("%d",&n);
	for(int i=2;i<=n;i++){
		lg[i]=lg[i>>1]+1;
	}
	for(int i=1;i<=n;i++) sf("%lld",&a[i]),stb[0][i]=a[i];
	for(int i=1;i<=lg[n];i++){
		for(int j=1;j+(1<<i)-1<=n;j++){
			stb[i][j]=max(stb[i-1][j],stb[i-1][j+(1<<(i-1))]);
		}
	}
	for(int i=1;i<=n;i++){
		while(top&&a[st[top]]<a[i])
			vec[st[top--]].push_back(i);
		for(int j=1;j<=top;j++){
			vec[st[j]].push_back(i);
		}
		st[++top]=i;
	}
	sf("%d",&t);
	for(int i=1;i<=t;i++) { sf("%d%d",&q[i].l,&q[i].r); q[i].id=i; }
	sort(q+1,q+t+1,cmp);
	build(1,1,n);
	for(int i=1;i<=t;i++){
		for(int j=q[i].l;j<=(i==1?n:q[i-1].l-1);j++){
			for(int x:vec[j]){
				if(x+x-j<=n)
				insert(1,1,n,a[j]+a[x],x+x-j);
			}
		}
		ans[q[i].id]=ask(1,1,n,q[i].l+2,q[i].r);
	}
	for(int i=1;i<=t;i++) pf("%lld\n",ans[i]);
}

标签:lg,法阵,max,ll,tree,mid,int
From: https://www.cnblogs.com/liyixin0514/p/18468581

相关文章

  • P2119 [NOIP2016 普及组] 魔法阵
    P2119[NOIP2016普及组]魔法阵传送门1我们可以先写出\(O(m^4)\)的暴力#include<bits/stdc++.h>#defineintlonglong#definePIIpair<int,int>usingnamespacestd;constintinf=0x3f3f3f3f;constintMOD=1e9+7,N=4e4+5;intn,m,ans[N......
  • 2016 CSP-J/NOIP万字长文复赛真题题解——秒杀T1 买铅笔,T2 回文日期,T3 海港,T4 魔法
    [NOIP2016普及组]买铅笔题干[NOIP2016普及组]买铅笔题目背景NOIP2016普及组T1题目描述P老师需要去商店买nnn支铅笔作为小朋友们参加NOIP的礼物。她发现......
  • CSP历年复赛题-P2119 [NOIP2016 普及组] 魔法阵
    原题链接:https://www.luogu.com.cn/problem/P2119题意解读:在一组数里找出所有的Xa,Xb,Xc,Xd的组合,使得满足Xa<Xb<Xc<Xd,Xb-Xa=2(Xd-Xc),Xb-Xa<(Xc-Xb)/3,并统计出每个数作为A,B,C,D出现的次数。解题思路:1、枚举(O(n^4))首先想到的是通过4重循环枚举所有可能的Xa,Xb,Xc,Xd,然后判......
  • 小樱的魔法阵
    importturtleastimporttimedeftcyuan(x,y,r):t.fillcolor("black")t.begin_fill()t.seth(0)y=y-rt.penup()t.goto(x,y)t.pendown()t.circle(r)t.end_fill()defyuan(x,y,r):t.seth(0)y......