求证明/求 hack
查看原帖
求证明/求 hack
239895
Yusani_huh楼主2022/11/20 19:30

我做法跟正解不大一样,思路如下:

假设当前在第 ii 个位置做到了一次取 max\max 操作,在这次操作之后第 ii 个位置上又加上的数的和为 sis_i。分类讨论:

因为一定有解,所以 aiaisia_i\le a'_i-s_i(和同学讨论后怀疑这里可能不一定),且有 max(ai,x)aisi\max(a_i,x)\le a'_i-s_i

ai=aisia_i=a'_i-s_i,那么当前操作就有 xaix\le a_i,即 xaisix\le a'_i-s_i

ai<aisia_i<a'_i-s_i,那么当前操作就有 xaisix\le a'_i-s_i

所以每次求得的 xx 都满足 xmini=lr(aisi)x\le \min_{i=l}^r{(a'_i-s_i)}

那么直接用线段树维护 aisia'_i-s_i,然后顺序操作,区间加直接加,求 xx 直接求区间 min\min

交上去 #5 AC,只有 10pts。代码:

#include<bits/stdc++.h>
using namespace std;
#define N 100003
#define LL long long
#define INF 0x3f3f3f3f
int T,n,q;
LL a[N],b[N];
struct opti{
	int op,l,r;
	LL x;
}p[N];
struct node{
	int l,r,mn,lz;
}t[N*4];
void build(int l,int r,int p){
	t[p].l=l,t[p].r=r;
	if(l==r){
		t[p].mn=b[l],t[p].lz=0;
		return;
	}
	int mid=l+r>>1;
	build(l,mid,p*2),build(mid+1,r,p*2+1);
	t[p].mn=min(t[p*2].mn,t[p*2+1].mn);
}
int lth(node p){return p.r-p.l+1;}
void psd(int p){
	int ls=p*2,rs=p*2+1;
	t[ls].mn+=t[p].lz,t[rs].mn+=t[p].lz;
	t[ls].lz+=t[p].lz,t[rs].lz+=t[p].lz;
	t[p].lz=0;
}
void add(int l,int r,LL a,int p){
	if(t[p].r<l||t[p].l>r) return;
	if(t[p].l>=l&&t[p].r<=r){
		t[p].mn+=a,t[p].lz+=a;
		return;
	}
	if(t[p].lz) psd(p);
	add(l,r,a,p*2),add(l,r,a,p*2+1);
	t[p].mn=min(t[p*2].mn,t[p*2+1].mn);
}
LL camn(int l,int r,int p){
	if(t[p].r<l||t[p].l>r) return INF;
	if(t[p].l>=l&&t[p].r<=r)
		return t[p].mn;
	return min(camn(l,r,p*2),camn(l,r,p*2+1));
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&q);
		for(int i=1;i<=n;++i)
			scanf("%lld",&a[i]);
		for(int i=1;i<=q;++i){
			scanf("%d%d%d",&p[i].op,&p[i].l,&p[i].r);
			if(p[i].op==1) scanf("%lld",&p[i].x);
		}
		for(int i=1;i<=n;++i)
			scanf("%lld",&b[i]);
		build(1,n,1);
		for(int i=1;i<=q;++i) //将所有加数减掉以维护a'_i-s_i
			if(p[i].op==1) add(p[i].l,p[i].r,-p[i].x,1);
		for(int i=1;i<=q;++i)
			if(p[i].op==1) add(p[i].l,p[i].r,p[i].x,1);
			else printf("%lld ",camn(p[i].l,p[i].r,1));
		puts("");
	}
	return 0;
}
2022/11/20 19:30
加载中...