CF671E(線段樹 單調棧)

2022-05-03 18:21:08 字數 2639 閱讀 7730

傳送門

神仙題,看題解看了乙個多小時才看懂

首先我們設\(pre_i\)和\(suf_i\)分別表示\(1\)到\(i\)需要的額外油量和\(i\)到\(1\)需要的額外油量,那麼有

\[\begin

pre_i=pre_-a_+w_\\

suf_i=suf_-a_i+w_

\end

\]從\(i\)向右走到\(j\)需要的額外油量是\(pre_j-pre_i\),從\(j\)向左走到\(i\)需要的額外油量是\(suf_j-suf_i\)

對於每個\(i\),我們算出\(nxt_i=\min\),即往右走時第乙個走不到的位置,用單調棧就可以處理。從\(nxt_i\)向\(i\)連一條邊,最終可以形成一棵樹

考慮如果要從\(i\)走到\(nxt_i\),那麼需要的最少油量是\(pre_-pre_i\),而由於我們之後還需要從右往左走回來,所以這些油量肯定是加到\(a_\)上是最優的,而對於\(i\)來說,它右邊需要加油量的所有位置,就是它在樹上的祖先的那些位置,所以我們可以在\(dfs\)這棵樹的時候,每次進入乙個子樹就加入貢獻,出去就減掉貢獻

設從\(l\)走到\(r\)的最小代價是\(calc(l,r)\),那麼乙個\(r\)合法,首先需要滿足\(calc(l,r)\leq k\)

由於我們加油量會對\(suf\)造成更改,我們設\(suf'\)表示更改後的\(suf\)

然後在考慮從\(r\)向左走到\(l\),剩下的油量肯定是全都加到\(r\)位置上最優,所以還需要滿足\(calc(l,r)+\max(0,suf'_r-\min\limits_\)會有乙個字尾減的貢獻(\(x-1\)及之後的元素)

ps:這就是為什麼要定義為左閉右開的形式,如果是閉區間,那麼\(r=x-1\)的時候就會非常麻煩,因為此時不能減,而左閉右開的時候涉及到\(x-1\)的\([l,r]\)顯然有\(r\geq x\),那麼可以直接區間減了

然後我們需要去掉區間的限制,把\([1,l-1]\)加上\(\infty\),而\([r_\max,n]\)加上\(-\infty\)即可(這裡需要是\(r_\max\),這樣才能使\(\min [l,r](r\geq r_\max)\)=-\infty)

然後我們考慮用線段樹維護最終的答案,記\(ans_p\)表示只考慮當前節點的\(\min\\),且\(r\)在右子樹中,此時最大的答案是多少,轉移類似於樓房重建那一題,在子樹里\(dfs\)

具體轉移細節看**,時間複雜度\(o(n\log^2 n)\)

//yuanquming

#include#define r register

#define fp(i,a,b) for(r int i=(a),i=(b)+1;ii;--i)

#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)

templateinline bool cmax(t&a,const t&b)

using namespace std;

typedef long long ll;

inline ll min(r ll x,r ll y)

const int n=1e5+5;const ll inf=1e18;

ll pre[n],suf[n],ans[n<<2],amn[n<<2],bmn[n<<2],t[n<<2];

int a[n],w[n],st[n],nxt[n],n,k,top,res;

#define lc (p<<1)

#define rc (p<<1|1)

#define ls lc,l,mid

#define rs rc,mid+1,r

inline void ppd(r int p,r ll x)

inline void pd(r int p)

ll calc(int p,int l,int r,ll x)

void update(int p,int l,int r,int ql,int qr,ll v)

ll qr(int p,int l,int r,ll x)

ll query(int p,int l,int r,ll &x)

int mid=(l+r)>>1;pd(p);

if(bmn[lc]=pre[st[top]])--top;

nxt[i]=st[top],st[++top]=i;

// to[nxt[i]].pb(i);

add(nxt[i],i);

} top=0;

}void dfs(int u)

int rmx=st[ret]-1;ll mn=inf;

if(u>1)update(1,1,n,1,u-1,inf);

update(1,1,n,rmx,n,-inf);

int pos=query(1,1,n,mn);

if(u>1)update(1,1,n,1,u-1,-inf);

update(1,1,n,rmx,n,inf);

cmax(res,pos-u+1);

}// for(auto v:to[u])dfs(v);

go(u)dfs(v);

if(nxt[u]<=n)update(1,1,n,nxt[u]-1,n,pre[nxt[u]]-pre[u]);

--top;

}int main()

單調棧 線段樹

sequence 時間限制 c c 3秒,其他語言6秒 空間限制 c c 524288k,其他語言1048576k 64bit io format lld your are given two sequences a1 na a1 n and b1 nb b1 n you need to answe...

做題 cf603E 線段樹分治

首先感謝題解小哥,他在標算外又總結了三種做法。此處僅提及最後一種做法。首先考慮題目中要求的所有結點度數為奇數的限制。對於每乙個聯通塊,因為所有結點總度數是偶數,所以總結點數也必須是偶數的。即所有聯通塊都要是偶數大小。而考慮任意乙個偶數大小的聯通塊,我們任意取它的乙個生成樹,然後進行如下演算法 設 1...

CF 242E 線段樹區間亦或 求和

題目鏈結 題意 給你n個數,有兩種操作,op 1,對從l到r的數求和,op 2,對從l到r的值xor val。思路 由於亦或是位運算,我們可以考慮位運算的關係,1 xor 1 0,0 xor 1 1,1 xor 0 1 0 xor 0 0 可以看出0 xor x x 1 xor 1 0,1 xor ...