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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

【无码专区10】第K大查询(双向链表 /主席树+st表)

發布時間:2023/12/3 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【无码专区10】第K大查询(双向链表 /主席树+st表) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

已自我實現,但還是歸入無碼專區序列。哈哈哈哈哈

對于my idea部分,我的每一個想法都實現了,可供參考。


problem

給定一個 1~n1\sim n1n 的排列和 kkk,求所有 r?l+1≥kr-l+1\ge kr?l+1k 的區間 [l,r][l,r][l,r] 中的第 kkk 大數,輸出他們的和。

60%,n≤5e460\%,n\le 5e460%,n5e4

20%,k=120\%,k=120%,k=1

100%,n≤5e5,k≤50100\%,n\le 5e5,k\le 50100%,n5e5,k50

128MB,1s128MB,1s128MB,1s

my idea

注意到 kkk 是非常小的,粗略來看正解應該是 O(nk)O(nk)O(nk) 的。

考慮單獨統計每個位置 iii 的貢獻。

則需要枚舉其左邊有 xxx>a[i]>a[i]>a[i] 的數字,右邊有 k?x?1k-x-1k?x?1 個。因為 aia_iai? 自己本身還要在前 kkk 大中占據一席位置。

然后需要知道兩個端點的位置,記為 l,rl,rl,r。即 [l,i)[l,i)[l,i)>a[i]>a[i]>a[i] 的數有 xxx 個,(i,r](i,r](i,r]>a[i]>a[i]>a[i] 的數有 k?x?1k-x-1k?x?1 個。

假設 lll 左邊,rrr 右邊第一個 >a[i]>a[i]>a[i] 的數位置為 L,RL,RL,R

?p∈(L,l],q∈[r,R)\forall\ p\in(L,l],q\in[r,R)??p(L,l],q[r,R) 的區間 [p,q][p,q][p,q] 的答案都會是 a[i]a[i]a[i]

問題就在于怎么快速求得位置左右大于 a[i]a[i]a[i] 個數為 x(1≤x<k)x(1\le x<k)x(1x<k) 的所有位置。

沒錯——還是主席樹!!!

弄兩個主席樹,一個表示前綴主席樹,一個表示后綴主席樹。

a[i]a[i]a[i] 建立權值線段樹作下標,樹上節點權值為下標 iii,維護最大值/最小值。

具體而言:以前綴樹為例。

查詢:查詢前 i?1i-1i?1 個樹的版本中 [ai+1,n][a_i+1,n][ai?+1,n] 的區間中的最大值。表示是最靠近 iii>a[i]>a[i]>a[i] 的位置。

假設這個位置是 ppp,接下來就是在 p?1p-1p?1 的版本中查詢 [ai+1,n][a_i+1,n][ai?+1,n] 的最大值,表示是第二靠近 iii>a[i]>a[i]>a[i] 的位置,以此類推,最多查詢到第 k?1k-1k?1 靠近就行了。

修改:直接在 i?1i-1i?1 的版本上新增將 a[i]a[i]a[i] 對應葉子權值修改為 iii,繼續維護最大值。

后綴樹與前綴樹同理,從后往前做,維護最小值即可。

因為枚舉順序不一樣所以是分開做,那么就可以共享同一棵主席樹,節省空間。

將每個位置的左右都預處理出來,時間復雜度為 O(nklog?n)O(nk\log n)O(nklogn) 的。

對于 60%60\%60% 的數據點 n≤5e4n\le 5e4n5e4,算出來大約是 4e74e74e7 空間也是綽綽有余的。

只不過如果 n≤5e5n\le 5e5n5e5,超時不說,連主席樹都開不出來這么大。

#include <bits/stdc++.h> using namespace std; #define maxk 52 #define maxn 50002 #define inf 0x3f3f3f3f int n, k, cnt; int lst[maxn][maxk], nxt[maxn][maxk]; int a[maxn], root[maxn]; struct node { int ans, lson, rson; } t[maxn * 25];#define mid ( ( l + r ) >> 1 )namespace pre_sgt {void build( int &now, int l, int r ) { t[now = ++ cnt].ans = 0;if( l == r ) return;build( t[now].lson, l, mid );build( t[now].rson, mid + 1, r );}void modify( int pre, int &now, int l, int r, int pos, int k ) {t[now = ++ cnt] = t[pre];if( l == r ) { t[now].ans = k; return; }if( pos <= mid ) modify( t[pre].lson, t[now].lson, l, mid, pos, k );else modify( t[pre].rson, t[now].rson, mid + 1, r, pos, k );t[now].ans = max( t[t[now].lson].ans, t[t[now].rson].ans );}int query( int now, int l, int r, int L ) {if( r < L ) return 0;if( L <= l ) return t[now].ans;return max( query( t[now].lson, l, mid, L ), query( t[now].rson, mid + 1, r, L ) );} }namespace suf_sgt {void build( int &now, int l, int r ) { t[now = ++ cnt].ans = inf;if( l == r ) return;build( t[now].lson, l, mid );build( t[now].rson, mid + 1, r );}void modify( int pre, int &now, int l, int r, int pos, int k ) {t[now = ++ cnt] = t[pre];if( l == r ) { t[now].ans = k; return; }if( pos <= mid ) modify( t[pre].lson, t[now].lson, l, mid, pos, k );else modify( t[pre].rson, t[now].rson, mid + 1, r, pos, k );t[now].ans = min( t[t[now].lson].ans, t[t[now].rson].ans );}int query( int now, int l, int r, int L ) {if( r < L ) return inf;if( L <= l ) return t[now].ans;return min( query( t[now].lson, l, mid, L ), query( t[now].rson, mid + 1, r, L ) );} }int main() {freopen( "kth.in", "r", stdin );freopen( "kth.out", "w", stdout );scanf( "%d %d", &n, &k );for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] );pre_sgt :: build( root[0], 1, n );for( int i = 1;i <= n;i ++ ) {lst[i][0] = i;int pos = i - 1;for( int j = 1;j <= k;j ++ ) {lst[i][j] = pre_sgt :: query( root[pos], 1, n, a[i] + 1 );if( ! lst[i][j] ) break;else pos = lst[i][j] - 1;}pre_sgt :: modify( root[i - 1], root[i], 1, n, a[i], i );}cnt = 0;suf_sgt :: build( root[n + 1], 1, n );memset( nxt, 0x3f, sizeof( nxt ) );for( int i = n;i;i -- ) {nxt[i][0] = i;int pos = i + 1;for( int j = 1;j <= k;j ++ ) {nxt[i][j] = suf_sgt :: query( root[pos], 1, n, a[i] + 1 );if( nxt[i][j] == inf ) break;else pos = nxt[i][j] + 1;}suf_sgt :: modify( root[i + 1], root[i], 1, n, a[i], i );}long long ans = 0;for( int i = 1;i <= n;i ++ ) {for( int j = 0;j < k;j ++ ) {int l = lst[i][j], r = nxt[i][k - j - 1];if( ! l or r == inf ) continue;int posl = max( 1, lst[i][j + 1] + 1 );int posr = min( n, nxt[i][k - j] - 1 );ans += 1ll * ( l - posl + 1 ) * ( posr - r + 1 ) * 1ll * a[i];}}printf( "%lld\n", ans );return 0; }

注意到還有 20%20\%20% 的額外特殊數據:k=1k=1k=1

這就相當于求每個數為一個區間的最大值的貢獻。

顯然 [l,r][l,r][l,r] 中最大的數假設在 midmidmid 處,則會將區間劃分成獨立的兩個子區間 [l,mid?1],[mid+1,r][l,mid-1],[mid+1,r][l,mid?1],[mid+1,r]

所以可以直接遞歸處理,對于區間內最大值的貢獻自然是 ?l≤i≤mid≤j≤r\forall\ l\le i\le mid\le j\le r??limidjr[i,j][i,j][i,j] 區間都合法。

一個區間內的最大值可以用 st表 預處理出來,查詢做到 O(1)O(1)O(1)

每個點就只會被當作最大值算一次,計算也是 O(1)O(1)O(1) 的。

時間復雜度在于 st表 的預處理,O(nlog?n)O(n\log n)O(nlogn)

namespace K1 {long long ret = 0;int query( int l, int r ) {int i = log( r - l + 1 ) / log( 2 );if( a[st[l][i]] < a[st[r - ( 1 << i ) + 1][i]] ) return st[r - ( 1 << i ) + 1][i];else return st[l][i]; }void dfs( int l, int r ) {if( l > r ) return;if( l == r ) { ret += a[l]; return; }int mid = query( l, r );ret += 1ll * ( mid - l + 1 ) * ( r - mid + 1 ) * a[mid];dfs( l, mid - 1 );dfs( mid + 1, r );}void solve() {for( int i = 1;i <= n;i ++ ) st[i][0] = i;for( int j = 1;( 1 << j ) <= n;j ++ )for( int i = 1;i + ( 1 << j ) - 1 <= n;i ++ )if( a[st[i][j - 1]] > a[st[i + ( 1 << j - 1 )][j - 1]] )st[i][j] = st[i][j - 1];elsest[i][j] = st[i + ( 1 << j - 1 )][j - 1];dfs( 1, n );printf( "%lld\n", ret );} }

將這兩份代碼拼湊在一起,就可以拿到 80′80'80 的高分了。

solution

事實上,我的想法已經非常接近正解了。

只是預處理不是采用主席樹自帶一個 log?\loglog ,而是使用雙向鏈表。

把所有元素從大到小操作,用雙向鏈表連接起來,每次新增一個數就會斷掉兩個位置的連接,然后加進去再連上。

鏈表 Li,RiL_i,R_iLi?,Ri? 記錄的是當前對于 iii 而言朝左朝右第一個比 a[i]a[i]a[i] 大的數為止,每次新增數都會維護這個鏈表。

暴力朝左朝右跳 kkk 次鏈表,將過程點記錄下來,然后就可以枚舉 xxx 計算了。

時間復雜度為 O(nk)O(nk)O(nk)

code

#include <bits/stdc++.h> using namespace std; #define maxn 500005 #define maxk 55 int n, k; long long ans; int lst[maxn], nxt[maxn], L[maxk], R[maxk], a[maxn], id[maxn]; set < int > s;int main() {freopen( "kth.in", "r", stdin );freopen( "kth.out", "w", stdout );scanf( "%d %d", &n, &k );for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] ), id[i] = i;sort( id + 1, id + n + 1, []( int x, int y ) { return a[x] > a[y]; } );for( int i = 1;i <= n;i ++ ) {int l = 0, r = 0, cnt_l = 0, cnt_r = 0, Last, Next;auto it = s.lower_bound( id[i] );if( it != s.end() ) r = *it, l = lst[r];else if( ! s.empty() ) l = *-- it, r = nxt[l];s.insert( id[i] );L[0] = R[0] = id[i], Last = l, Next = r;for( int j = 1;j <= k and l;j ++ ) L[++ cnt_l] = l, l = lst[l];for( int j = 1;j <= k and r;j ++ ) R[++ cnt_r] = r, r = nxt[r];if( cnt_l < k ) L[++ cnt_l] = 0;if( cnt_r < k ) R[++ cnt_r] = n + 1;for( l = k - 1, r = 0;~ l;l --, r ++ )if( l < cnt_l and r < cnt_r )ans += 1ll * ( L[l] - L[l + 1] ) * ( R[r + 1] - R[r] ) * a[id[i]];lst[id[i]] = Last, nxt[id[i]] = Next;if( Last ) nxt[Last] = id[i];if( Next ) lst[Next] = id[i];}printf( "%lld\n", ans );return 0; }

總結

以上是生活随笔為你收集整理的【无码专区10】第K大查询(双向链表 /主席树+st表)的全部內容,希望文章能夠幫你解決所遇到的問題。

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