1. 程式人生 > >HDU 6058

HDU 6058

鏈表 font 維護 nbsp col != turn ret 代碼

題意略。

思路:這題要求第k大之和,我們可以算每個數字對答案的貢獻。這次,我們采取一個特殊的手法:我們按數字的大小,從小到大來計算貢獻。

先從1開始計算,我們知道,1周圍的數字都比1大,假設1在位置p,那麽它能做出貢獻的區間是:[p - k + 1,p],[p - k + 2,p + 1],...,[p,p + k -1]。

如果每一個數字都能像1這樣就好了,從而我們想到用一個雙向鏈表來維護這個體系,每計算完一個數字我們就把這個數字從我們的鏈表中刪去,

這樣就可以總是保證當前計算的那個數字周圍的數都比它大(有點像離線的思想)。

詳見代碼:

#include<bits/stdc++.h>
typedef 
long long LL; const LL maxn = 5 * 1e5 + 5; int lft[maxn],rht[maxn],mp[maxn]; int store1[100],store2[100],s,t; int n,k; void dlt(int pos){ int l = lft[pos],r = rht[pos]; rht[l] = r,lft[r] = l; } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&k);
int temp; for(int i = 1;i <= n;++i){ lft[i] = i - 1,rht[i] = i + 1; scanf("%d",&temp); mp[temp] = i; } LL ans = 0; for(int i = 1;i <= n;++i){ memset(store1,-1,sizeof(store1)); memset(store2,-1,sizeof
(store2)); s = 0,t = 0; int pos = mp[i]; for(int j = 0,ptr = pos;j < k + 1 && ptr;ptr = lft[ptr],++j) store1[++s] = ptr; for(int j = 0,ptr = pos;j < k + 1 && ptr != n + 1;ptr = rht[ptr],++j) store2[++t] = ptr; for(int j = s;j >= 1;--j){ LL L,R; int l1 = store1[j],l2 = store1[j + 1]; L = (l2 == -1 ? l1 : l1 - l2); int r1 = store2[k - j + 1],r2 = store2[k - j + 1 + 1]; if(r1 == -1) R = 0; else R = (r2 == -1 ? n - r1 + 1 : r2 - r1); ans += L * R * i; } dlt(pos); } printf("%lld\n",ans); } return 0; }

HDU 6058