日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[HNOI2016] 序列(线段树 + 莫队 + 倍增)

發布時間:2023/12/3 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [HNOI2016] 序列(线段树 + 莫队 + 倍增) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

problem

luogu-P3246

心路歷程+卡常歷程+問題存疑

一直在想莫隊的做法。發現左右指針的移動對應一段左/右端點固定的子序列,然后可以一個數代表一段相同的貢獻。

就開始求 lsti,nxtilst_i,nxt_ilsti?,nxti? 了。

仔細想想需要找到 lstlsti<l≤lstilst_{lst_i}<l\le lst_ilstlsti??<llsti? 的特殊位置,然后要知道這中間點的貢獻。

我就在來了個線段樹維護區間和,然后用 set\text{set}setlstilst_ilsti? 二分找。

后來半天過不了樣例,不是過大就是過小。

隱約間覺得不對勁,后來真的好像不對勁,我就大刀闊斧直接砍了。

開始懷疑自己的莫隊算法。此時經過了八點半到九點十分共四十多分鐘的時間。

我開始嘗試整體二分,后面發現自己完全不會分治。此時我陷入了困境。

我又倒回去想了下莫隊,我發現問題出在 lstilst_ilsti? 中間不是所有點都是對應在 [l,r][l,r][l,r] 的貢獻,我好像算多了。

因為 lstilst_ilsti? 是從 iii 出發得到的,而非整體的 rrr

難道我只用維護一個全局的?不,這樣會算少。

突然我意識到可以把這種關系建成樹!并且發現了編號之間與子序列間的對應關系。

此時時間大概在酒店四五十左右,我就開始敲了。

過程比較順利,十點半不到?就敲完了。然后開始調,輸出中間結果,發現最后結果已經和樣例快要吻合時,我就更加堅定了自己的做法。

十一點我通過了小樣例,緊接著大樣例也是正確的。

但很遺憾,耗時 3.6s3.6s3.6s 多。

我發現輸入數據過多,就換成了快讀快輸。耗時變成了 3.0s3.0s3.0s 多。

接著我覺得倍增對于極限數據也只會到 161616 ,所以卡了下倍增的循環。耗時縮減為 2.6s2.6s2.6s

改下塊長加 O2O2O2 就能看看卡過。

但是當時我嘗試了一下用莫隊的奇偶優化,時間瞬間到了 1.9s1.9s1.9s 左右。

此時出現了個大問題!我發現這樣就跑不過大樣例了。

我對此非常疑惑。

下午又研究了一番,發現當我將莫隊的四個指針移動前后兩個交換位置,也會跑不過大樣例?!!莫隊詢問排序塊內按右端點降序排列也不行!

這下真給我整不會了,我突然猜是不是爆 long long 了,用 assert\text{assert}assert 發現沒有,自己算極限也是 6e186e186e18 不到。

后面通過數據,我覺得應該是倍增到最后不能往上跳部分出現了問題。

但是這樣怎么可能會讓我的第一次建構代碼跑過去呢?

至今我仍然不清楚。

只能說非常幸運的在第一次敲代碼的時候就跑過去了把。

如果有人知道這是為什么的話,麻煩請聯系一下我!謝謝了。

solution

由于昨天做的一道題題解提了一句莫隊二次離線,形式就是 l≤x≤y≤rl\le x\le y\le rlxyr

所以一看到這個題,我就在想莫隊的做法。

莫隊反正就是左端點分塊排序,右端點塊內遞增,就兩個指針移動的板子,時間復雜度就帶了個 n\sqrt{n}n?

問題是如何快速計算移動 l/rl/rl/r 一個位置后對應的答案動態變化。

以右移 rrr 為例,當 r←r+1r\leftarrow r+1rr+1的時候,我們需要新加 [l,r]~r+1[l,r]\sim r+1[l,r]r+1r?l+1r-l+1r?l+1 組新的子序列貢獻。

然后發現,我們只需要跳較小值的位置即可。

具體而言,假設 ppp 滿足 ap≤ar+1∧?p<i<r+1ai>ar+1a_p\le a_{r+1}\wedge \forall_{p<i<r+1}\ a_i>a_{r+1}ap?ar+1??p<i<r+1??ai?>ar+1?

最大的不超過 ar+1a_{r+1}ar+1? 的位置,記為 lstr+1lst_{r+1}lstr+1?

則對于 [p,r]~r+1[p,r]\sim r+1[p,r]r+1r?p+1r-p+1r?p+1 組子序列的貢獻都是 apa_pap?

同理我們找到 lstplst_plstp?,然后共 p?lstp+1p-lst_p+1p?lstp?+1 組子序列的貢獻都是 alstpa_{lst_p}alstp??

以此類推…\dots直到跳到 <l<l<l 的位置,lll 開頭那一段貢獻可能不完整,簡單處理一下即可。

我們肯定不能暴力跳,注意到一個數對應的完整區間貢獻是固定的,所以我們可以倍增!

我們將 lsti→ilst_i\rightarrow ilsti?i 建立有向邊,然后生成一棵樹,不難發現。(u?fa)?au(u-fa)*a_u(u?fa)?au? 即為 uuu 對應的完整貢獻。

至于找 lstilst_ilsti?,答主比較無腦,直接權值線段樹上,完全不帶腦子的大常數。而且 aia_iai? 值域過大,還加了個離散化。

rrr 左移減去貢獻與上面的過程完全一樣。

lll 的左移右移則需要維護 nxtinxt_inxti?,最小的不大于 aia_iai? 的位置。獲得方式同 lstilst_ilsti?

同樣 nxti→inxt_i\rightarrow inxti?i 建立有向邊,生成一棵樹。

為了使得點與父節點之間能算出點的貢獻,我們欽定找不到的 lsti=0,nxti=n+1lst_i=0,nxt_i=n+1lsti?=0,nxti?=n+1

時間復雜度大概是 O(nnlog?n)O(n\sqrt n\log n)O(nn?logn)

會被卡,那就稍微調整一下塊長。

code

#include <bits/stdc++.h> using namespace std; #define lson now << 1 #define rson now << 1 | 1 #define mid (l + r >> 1) #define ll long long #define maxn 100005 int n, Q, B; ll ans; int a[maxn], b[maxn], block[maxn], lst[maxn], nxt[maxn]; ll ret[maxn]; struct query { int l, r, id; }q[maxn];namespace MaxSgt {int Max[maxn << 2];void modify( int now, int l, int r, int p, int v ) {if( l == r ) { Max[now] = v; return; }if( p <= mid ) modify( lson, l, mid, p, v );else modify( rson, mid + 1, r, p, v );Max[now] = max( Max[lson], Max[rson] );}int query( int now, int l, int r, int L, int R ) {if( R < l or r < L ) return 0;if( L <= l and r <= R ) return Max[now];return max( query( lson, l, mid, L, R ), query( rson, mid + 1, r, L, R ) );} }namespace MinSgt {int Min[maxn << 2];void build( int now, int l, int r ) {Min[now] = n + 1;if( l == r ) return;build( lson, l, mid );build( rson, mid + 1, r );}void modify( int now, int l, int r, int p, int v ) {if( l == r ) { Min[now] = v; return; }if( p <= mid ) modify( lson, l, mid, p, v );else modify( rson, mid + 1, r, p, v );Min[now] = min( Min[lson], Min[rson] );}int query( int now, int l, int r, int L, int R ) {if( R < l or r < L ) return n + 1;if( L <= l and r <= R ) return Min[now];return min( query( lson, l, mid, L, R ), query( rson, mid + 1, r, L, R ) );} }struct tree {vector < int > G[maxn];int f[maxn][17]; ll g[maxn][17];void addedge( int u, int v ) {G[u].push_back( v );}int Fabs( int x ) {return x < 0 ? -x : x;}void dfs( int u, int fa ) {f[u][0] = fa, g[u][0] = 1ll * Fabs( u - fa ) * b[a[u]]; for( int i = 1;i < 17;i ++ ) {f[u][i] = f[f[u][i - 1]][i - 1];g[u][i] = g[f[u][i - 1]][i - 1] + g[u][i - 1];}for( int i = 0;i < G[u].size();i ++ ) dfs( G[u][i], u );} }L, R;void AddR( int l, int r ) {for( int i = 16;~ i;i -- )if( L.f[r][i] >= l ) ans += L.g[r][i], r = L.f[r][i];ans += 1ll * (r - l + 1) * b[a[r]]; }void SubR( int l, int r ) {for( int i = 16;~ i;i -- )if( L.f[r][i] >= l ) ans -= L.g[r][i], r = L.f[r][i];ans -= 1ll * (r - l + 1) * b[a[r]]; } void AddL( int l, int r ) {for( int i = 16;~ i;i -- )if( R.f[l][i] <= r ) ans += R.g[l][i], l = R.f[l][i];ans += 1ll * (r - l + 1) * b[a[l]]; }void SubL( int l, int r ) {for( int i = 16;~ i;i -- )if( R.f[l][i] <= r ) ans -= R.g[l][i], l = R.f[l][i];ans -= 1ll * (r - l + 1) * b[a[l]]; }void read( int &x ) {x = 0; int f = 1; char s = getchar();while( s < '0' or s > '9' ) {if( s == '-' ) f = -1; s = getchar();}while( '0' <= s and s <= '9' ) {x = (x << 1) + (x << 3) + (s ^ 48);s = getchar();}x *= f; }void print( ll x ) {if( x < 0 ) putchar('-'), x = -x;if( x > 9 ) print( x / 10 );putchar( x % 10 + '0' ); }int main() {read( n ), read( Q );B = 400;for( int i = 1;i <= n;i ++ ) read( a[i] );for( int i = 1;i <= Q;i ++ ) read( q[i].l ), read( q[i].r ), q[i].id = i;for( int i = 1;i <= n;i ++ ) block[i] = (i - 1) / B + 1;sort( q + 1, q + Q + 1, []( query x, query y ) { return (block[x.l] == block[y.l]) ? (x.r < y.r) : (x.l < y.l); } );for( int i = 1;i <= n;i ++ ) b[i] = a[i];sort( b + 1, b + n + 1 );int m = unique( b + 1, b + n + 1 ) - b - 1;for( int i = 1;i <= n;i ++ ) a[i] = lower_bound( b + 1, b + m + 1, a[i] ) - b;for( int i = 1;i <= n;i ++ ) {lst[i] = MaxSgt :: query( 1, 1, m, 1, a[i] );MaxSgt :: modify( 1, 1, m, a[i], i );L.addedge( lst[i], i );}MinSgt :: build( 1, 1, m );for( int i = n;i >= 1;i -- ) {nxt[i] = MinSgt :: query( 1, 1, m, 1, a[i] );MinSgt :: modify( 1, 1, m, a[i], i );R.addedge( nxt[i], i );}L.dfs( 0, 0 );R.dfs( n + 1, n + 1 );int curl = 1, curr = 0;for( int i = 1;i <= Q;i ++ ) {int l = q[i].l, r = q[i].r;while( curr < r ) AddR( curl, ++ curr );while( curr > r ) SubR( curl, curr -- );while( curl > l ) AddL( -- curl, curr );while( curl < l ) SubL( curl ++, curr );ret[q[i].id] = ans;}for( int i = 1;i <= Q;i ++ ) print( ret[i] ), putchar('\n');return 0; }

總結

以上是生活随笔為你收集整理的[HNOI2016] 序列(线段树 + 莫队 + 倍增)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。