1. 程式人生 > >hdu 6058 思維

hdu 6058 思維

return 例子 cstring sca 計算 ase 雙鏈表 += code

題目:http://acm.hdu.edu.cn/showproblem.php?pid=6058

分析題目的時候,由於枚舉的區間很多,而第k大的值範圍小,應該要想到去枚舉第k大的值然後找到這個值對答案的貢獻。

題解:我們只要求出對於一個數x左邊最近的k個比他大的和右邊最近k個比他大的,掃一下就可以知道有幾個區間的kk大值是xx.(這個地方自己舉幾個例子就知道了)

我們考慮從小到大枚舉x,每次維護一個鏈表(我寫了一個雙鏈表),鏈表裏只有大於x的數,每次求x對答案的貢獻的時候,直接在鏈表中x的位置左右k個值掃一邊就可以了。

ac代碼:

#include <cstdio>
#include 
<iostream> #include <cstring> using namespace std; typedef long long ll; struct node { ll key; ll l,r; }num[500001]; ll a[500001]; ll pos[500001]; ll pre[81],nex[81]; int main() { int t; scanf("%d",&t); while(t--) { int n,k; scanf("%d %d",&n,&k); num[
1].l=-1; num[n].r=-1; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); if(i!=1) num[i].l=i-1; if(i!=n) num[i].r=i+1; num[i].key=a[i]; pos[a[i]]=i; } ll sum=0; for(int i=1;i<=n;i++) { fill(pre
+1,pre+k+1,-1); fill(nex+1,nex+k+1,-1); ll po=pos[i]; ll temp=num[po].l; int ret=1; pre[0]=nex[0]=po; while(temp!=-1 && ret<=k) { pre[ret++]=temp; temp=num[temp].l; } temp=num[po].r; ret=1; while(temp!=-1 && ret<=k) { nex[ret++]=temp; temp=num[temp].r; } /* for(int i=1;i<=k;i++) cout<<pre[i]<<‘ ‘; cout<<endl; for(int i=1;i<=k;i++) cout<<nex[i]<<‘ ‘; cout<<endl; */ for(int j=0;j<k;j++) { if(pre[k-j-1]==-1 || nex[j]==-1) continue; ll ans=pre[k-j-1]-pre[k-j]; ll ans2=nex[j+1]-nex[j]; if(pre[k-j]==-1) ans=pre[k-j-1]; if(nex[j+1]==-1) ans2=n-nex[j]+1; // cout<<ans<<‘ ‘<<ans2<<endl; sum+=ans*ans2*(ll)i; } // delete ll nl=num[po].l; ll nr=num[po].r; num[nl].r=nr; num[nr].l=nl; } printf("%lld\n",sum); } return 0; }

用鏈表去維護左右比x大的數還是很神奇的(從小到大去計算貢獻值,每次算完小的值就從鏈表裏面刪去,,,, 厲害了)。。這個真心沒想到

hdu 6058 思維