1. 程式人生 > 實用技巧 >【省選】SNOI2020_區間和_LOJ3325/LuoguP6792_線段樹/勢能分析/SegBeats

【省選】SNOI2020_區間和_LOJ3325/LuoguP6792_線段樹/勢能分析/SegBeats

連結

LOJ3325
LuoguP6792

題解

xyx的題解

復讀機:

  • 發現如果要實現區間取max,區間求和,都必須使用吉司機線段樹,那這道題就大概率是要在吉司機線段樹的基礎上魔改而來。
  • 如果要求區間最大子段和,一定要記錄最大字首,最大字尾,區間和,最大子段和。
  • 我們發現吉司機線段樹的第一種情況:modify<node.min,此時顯然可以直接退出,第三種情況,modify>=node.min_2nd,直接遞迴下去顯然沒有問題。
  • 那就要討論一下第二種情況。我們發現我們這個節點的修改過後的最大字首的值大概率是原值加上最大字首中整個區間最小值的個數乘最小值的增量。
    • 什麼時候不是?可能存在另外一個字首,數字和當前沒有現在的最大字首大,但是裡面的區間最小值多,一取max就成為新的最大字首了。最大字尾和最大子段和同理。
    • 怎麼辦?我們可以對線段樹上每個節點記錄下來【以他為根的子樹中每個點的這三個量】當區間取max最小取到多少的時候會變化。那麼如果我們當前的tag大於等於這個閾值,同樣參照吉司機線段樹的第三種情況遞迴下去。
    • 為此,我們必須再記錄一下每個最大字首、最大字尾、最大子段和中的區間最小值個數。
  • 時間複雜度是什麼?
    • 我們額外的操作是不會影響吉司機線段樹的複雜度分析的。所以我們只用分析因為閾值而遞迴兩子樹的時候的複雜度。
    • 首先分析最大字首。記勢能函式\(\varphi\)為所有節點的最大字首長度之和。我們發現每次因為最大字首改變而下推標記總會使得這個勢能函式至少增加1(因為只有區間取max,最長字首的長度是單調不降的)。\(\varphi\)
      每增大1,需要花費\(O(\log n)\)的時間,\(\varphi\)最大為\(O(n\log n)\),故這裡的時間為\(O(n\log^2n)\)
    • 最大字尾和最大字首對稱,也為\(O(n\log^2n)\)
    • 最大子段和,會發現如果是因為最大字首和最大字尾而改動的最大子段和,顯然複雜度可以和他們一起統計。
    • 如果所有孩子和自己的字首、字尾都沒有變而最大子段和變了,那最多隻會變2次。所以複雜度依舊為\(O(n\log^2n)\)
    • 綜上,時間為\(O(n\log^2n)\)
  • 這道題的程式碼難度基本上在update上。

程式碼

#include<bits/stdc++.h>
#define LL long long
#define MAXN 101000
#define INF 1000000100
using namespace std;
template<typename T> void Read(T &cn)
{
	char c; int sig = 1;
	while(!isdigit(c = getchar())) if(c == '-') sig = 0;
	if(sig) {cn = c-48; while(isdigit(c = getchar())) cn = cn*10+c-48; }
	else    {cn = 48-c; while(isdigit(c = getchar())) cn = cn*10+48-c; }
}
template<typename T> void Write(T cn)
{
	int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
	if(cn < 0 || cx < 0) {putchar('-'); cn = 0-cn; cx = 0-cx; }
	while(cn)cm = cm*10+cn%10,cn/=10,wei++;
	while(wei--)putchar(cm%10+48),cm/=10;
	putchar(cx+48);
}
template<typename T> void WriteL(T cn) {Write(cn); puts(""); }
template<typename T> void WriteS(T cn) {Write(cn); putchar(' '); }
template<typename T> void Max(T &cn, T cm) {cn = cn < cm ? cm : cn; }
template<typename T> void Min(T &cn, T cm) {cn = cn < cm ? cn : cm; }
struct Seg{
	struct qwe{
		LL val; int shu;
		qwe() {val = -INF; shu = 0; }
		qwe(LL cn, int cm) {val = cn; shu = cm; }
		inline friend bool operator <(qwe a, qwe b) {return a.val < b.val; }
		inline friend qwe operator +(qwe a, qwe b) {a.val+=b.val; a.shu+=b.shu; return a; }
		void zeng(int cn) {val = val + 1ll*cn*shu; }
	};
	struct node{
		int xi, cixi;
		qwe he, pre, suf, duan;
		LL p_max, mintime;
		void qing()
		{
			xi = cixi = INF; 
			he = pre = suf = duan = qwe(0, 0);
			p_max = -INF; mintime = INF;
		}
		void set(int cn)
		{
			xi = cn; cixi = INF; 
			he = pre = suf = duan = qwe(cn, 1);
			p_max = cn; mintime = INF;
		}
		void up_xi(int cn) {if(xi==cn) return; xi = cn; he.shu = pre.shu = suf.shu = duan.shu = 0; }
	};
	node t[MAXN*4+1];
	int n;
	LL suan(qwe xian, qwe nex) {return nex.shu <= xian.shu ? INF : (xian.val-nex.val)/(nex.shu-xian.shu); }
	void update(node &cn, node ls, node rs)
	{
		cn.xi = min(ls.xi, rs.xi);
		cn.cixi = min(ls.cixi, rs.cixi);
		if(ls.xi < rs.xi) Min(cn.cixi, rs.xi);
		if(rs.xi < ls.xi) Min(cn.cixi, ls.xi);
		ls.up_xi(cn.xi);
		rs.up_xi(cn.xi);
		cn.he = ls.he + rs.he;
		cn.duan = max(max(ls.duan, rs.duan), ls.suf+rs.pre);
		cn.pre = max(ls.pre, ls.he+rs.pre);
		cn.suf = max(rs.suf, rs.he+ls.suf);
		cn.mintime = min(min(ls.mintime, rs.mintime), cn.xi+min(suan(cn.pre, ls.he+rs.pre), suan(cn.suf, rs.he+ls.suf)));
		Min(cn.mintime, cn.xi+min(min(suan(cn.duan, ls.duan), suan(cn.duan, rs.duan)), suan(cn.duan, ls.suf+rs.pre)));
	}
	void tui(int cn, int l, int r)
	{
		int ls = cn<<1, rs = ls|1, zh = (l+r)>>1;
		dfs_da(ls, l, zh, t[cn].p_max); dfs_da(rs, zh+1, r, t[cn].p_max); 
		t[cn].p_max = -INF;
	}
	void dfs_da(int cn, int l, int r, int cm)
	{
		if(t[cn].xi >= cm) return;
		if(t[cn].cixi > cm && cm < t[cn].mintime) {
			t[cn].he.zeng(cm-t[cn].xi); t[cn].duan.zeng(cm-t[cn].xi);
			t[cn].pre.zeng(cm-t[cn].xi); t[cn].suf.zeng(cm-t[cn].xi);
			t[cn].xi = cm; t[cn].p_max = cm;
			return;
		}
		t[cn].p_max = cm;
		tui(cn, l, r); update(t[cn], t[cn<<1], t[(cn<<1)|1]);
	}
	void build(int cn, int l, int r, int a[])
	{
		t[cn].qing(); if(l == r) {t[cn].set(a[l]); return; }
		int zh = (l+r)>>1; build(cn<<1,l,zh,a); build((cn<<1)|1,zh+1,r,a);
		update(t[cn], t[cn<<1], t[(cn<<1)|1]);
	}
	void gai_da(int cn, int cm, int cl, int cr, int l, int r)
	{
		if(cl <= l && r <= cr) {dfs_da(cn,l,r,cm); return; }
		int zh = (l+r)>>1; tui(cn,l,r);
		if(cl <= zh) gai_da(cn<<1, cm, cl, cr, l, zh);
		if(cr >  zh) gai_da((cn<<1)|1, cm, cl, cr, zh+1, r);
		update(t[cn], t[cn<<1], t[(cn<<1)|1]);
	}
	void qiu(int cn, int cl, int cr, int l, int r, node &ans)
	{
		if(cl <= l && r <= cr) {update(ans, ans, t[cn]); return; }
		int zh = (l+r)>>1; tui(cn, l, r);
		if(cl <= zh) qiu(cn<<1, cl, cr, l, zh, ans);
		if(cr >  zh) qiu((cn<<1)|1, cl, cr, zh+1, r, ans);
	}
	void build(int cn, int a[]) {n = cn; build(1,1,n,a); }
	void gai_da(int cl, int cr, int cm) {gai_da(1,cm,cl,cr,1,n); }
	LL qiu(int cl, int cr) {node guo; guo.qing(); qiu(1,cl,cr,1,n,guo); return guo.duan.val; }
}T;
int n,q;
int a[MAXN+1];
int main()
{
//	freopen("hack1.in","r",stdin);
//	freopen(".out","w",stdout);
	Read(n); Read(q);
	for(int i = 1;i<=n;i++) Read(a[i]);
	T.build(n, a);
	for(int i = 1;i<=q;i++)
	{
		int typ, bl, br, bx; Read(typ); Read(bl); Read(br);
		if(typ == 0) Read(bx), T.gai_da(bl,br,bx);
		if(typ == 1) WriteL(T.qiu(bl,br));
	}
	return 0;
}