KTT

写课件写着写着开始学 KTT 了是怎么回事。

KTT 解决的基本问题形如,给定 \(n\) 个一次函数 \(k_ix_i+b_i\),区间对 \(x_i\) 加正数,查询区间一次函数的值的最大值。

考虑尝试维护每个位置的最大线段交换的阈值 \(t\),即这个位置的儿子两个线段 \(l_1,l_2\),目前是 \(l_1\)\(\max\),对 \(x_i\) 再加超过 \(t\) 就变成 \(l_2\)\(\max\) 了。这个显然在 pushup 的时候要取 \(\min\)

然后区间加 \(x\) 的时候就直接递归到 \(t\geq x\) 的节点打上标记就行了,记得把阈值减去 \(x\)

可以证明时间复杂度是 \(O((n+q)\log^3 n)\) 的,但是因为目前 OI 界还不会卡,所以复杂度可以理解为常数略大一点的 2log。

P5693 EI 的第六分块

给定一个整数序列 \(a\),支持两种操作:

  • 1 l r x 表示给区间 \([l,r]\) 中每个数加上 \(x\)
  • 2 l r 表示查询区间 \([l,r]\) 的最大子段和(可以为空)

\(1\le n,q \le 4\times 10^5\)\(|a_i| \le 10^9\)\(1 \le x \le 10^6\)

如果把区间加变成单点加,我们就可以用正常的线段树来维护最大子段和,我们此时需要维护 \((lmx,rmx,ans,sum)\)

把这四个信息看成一次函数放到 KTT 上,阈值变成 使得任意函数的取值来源变化的最小的 \(x\)。合并之类的都是一样正常合并就行了,只需要注意修改的时候递归到合适的位置即可。

合并的时候,加法就是单纯的把真实值和斜率加起来,所以我们不显式维护 \(x_i\) 的值,而是只维护斜率和真实值。下放标记的时候,不难发现贡献可以直接放到真实值上,因为这里的斜率就是这个信息所对应的区间的长度。

EI 证明了这样做的时间复杂度是 3log 的。

事实上,因为 OI 界目前不会卡这个东西,涉及到 KTT 并且只加正数或者只加负数的标记大概都可以看做是正确的复杂度。

P6792 [SNOI2020] 区间和

有一个长度为 \(n\) 的整数数列 \(a_1,a_2,\cdots,a_n\)(可能含有负数)。现在对其进行 \(q\) 次操作,每次操作是以下二者之一:

  • 0 l r x 表示对于 \([l,r]\),将 \(a_i\) 赋值为 \(\max(a_i,x)\)
  • 1 l r 求区间 \([l,r]\) 的最大子段和。即:\(\max(0, \max_{l\le u\le v\le r} (\sum_{i=u}^v a_i))\)

\(1\le n\le10^5, 1\le q\le 2\times 10^5, |a_i|, |x|\le 10^9\)

加强的疑似有点多了。

首先取 \(\max\) 部分用 segbeats 做,问题变成区间的所有 \(\min\) 加上某个正数。

首先显然这两部分是完全独立的计算复杂度的。

注意到如果直接按上面做,这个操作是不能直接贡献到 \(x_i\) 的。所以我们把斜率维护成 \(\min\) 值个数(同时注意更新斜率对应的 \(\min\) 到底是什么),这样就能直接贡献了。虽然没人证,但是普遍认为这样复杂度还是 3log 的。

说实话我是真的不想写这个题代码,太史了,写错一个地方调半年啊。

AT 的某个题

会在课件亮相。是一个 KTT 优化 DP 题,伊娜做过。

板子

最后贴个板子下班。

const int M=5e5+5;
int a[M],pre[M];
int val[M],vcnt;
#define ll long long
#define fi first
#define se second
#define mid ((l+r)>>1)
struct KTT{
	struct line{ll k,b;};
	inline static pair<line,ll> mer(const line &x,const line &y){
		if(x.k==y.k){
			if(x.b>y.b)return {x,1e18};
			else return {y,1e18};
		}
		if(x.k>y.k){
			if(x.b>=y.b)return {x,1e18};
			else return {y,(y.b-x.b)/(x.k-y.k)};
		}
		if(x.b>y.b)return {x,(x.b-y.b)/(y.k-x.k)};
		return {y,1e18};
	}
	struct node{
		line x;ll t;
		inline node operator +(const node &y)const{
			node res;
			auto tmp=mer(x,y.x);
			res.t=min({t,y.t,tmp.se});
			res.x=tmp.fi;
			return res;
		}
	}xds[M<<2];
	ll tg[M<<2],tgb[M<<2];
	inline void pushup(int now){
		xds[now]=xds[now<<1]+xds[now<<1|1];
		return;
	}
	inline void upd(int now,ll w){
		xds[now].t-=w;
		xds[now].x.b+=xds[now].x.k*w;
		tg[now]+=w;
		return;
	}
	inline void add(int now,ll w){
		xds[now].x.b+=w;
		tgb[now]+=w;
		return;
	}
	inline void pushdown(int now){
		if(tg[now]){
			upd(now<<1,tg[now]),upd(now<<1|1,tg[now]);
			tg[now]=0;
		}
		if(tgb[now]){
			add(now<<1,tgb[now]),add(now<<1|1,tgb[now]);
			tgb[now]=0;
		}
		return;
	}
	inline void mdf(int now,int l,int r,int pos,ll v){
		if(l==r){
			xds[now].x.b=max(xds[now].x.b,v);
			return;
		}
		pushdown(now);
		if(pos<=mid)mdf(now<<1,l,mid,pos,v);
		else mdf(now<<1|1,mid+1,r,pos,v);
		return pushup(now);
	}
	inline void add(int now,int l,int r,int sl,int sr,ll w){
		if(sl<=l&&r<=sr)return add(now,w);
		pushdown(now);
		if(sl<=mid)add(now<<1,l,mid,sl,sr,w);
		if(sr>mid)add(now<<1|1,mid+1,r,sl,sr,w);
		return pushup(now);
	}
	inline void upd(int now,int l,int r,ll w){
		if(xds[now].t>=w)return upd(now,w);
		pushdown(now);
		upd(now<<1,l,mid,w),upd(now<<1|1,mid+1,r,w);
		return pushup(now);
	}
	inline void addv(int now,int l,int r,int sl,int sr,ll w){
		if(sl<=l&&r<=sr)return upd(now,l,r,w);
		pushdown(now);
		if(sl<=mid)addv(now<<1,l,mid,sl,sr,w);
		if(sr>mid)addv(now<<1|1,mid+1,r,sl,sr,w);
		return pushup(now);
	}
	inline ll ask(int now,int l,int r,int sl,int sr){
		if(sl<=l&&r<=sr)return xds[now].x.b;
		pushdown(now);
		if(sl>mid)return ask(now<<1|1,mid+1,r,sl,sr);
		if(sr<=mid)return ask(now<<1,l,mid,sl,sr);
		return max(ask(now<<1,l,mid,sl,sr),ask(now<<1|1,mid+1,r,sl,sr));
	}
	inline void build(int now,int l,int r){
		if(l==r){
			xds[now].x.k=val[l];
			xds[now].x.b=-1e18;
			xds[now].t=1e18;
			return;
		}
		build(now<<1,l,mid),build(now<<1|1,mid+1,r);
		return pushup(now);
	}
}T;
posted @ 2024-12-20 19:08  xingyu_xuan  阅读(35)  评论(3编辑  收藏  举报