第三場 hdu 6058 Kanade's sum(思惟題+set)

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

題目大意:把一個數組分紅若干子部分,求每部分中第k大的數的和是多少?ios

 

解題思路:首先可以想到的最暴力的算法是:枚舉數組的每個子部分二重for循環,而後求出每一段的第k大的數又是一重for循環。這樣的想法時間複雜度O(n^3)c++

看一下數據就知道上面的想法確定是不行的。算法

而後可以想到的還有就是對於數組中的數x,咱們只要找到x的左邊有k個比x大的數的位置,而後在右邊找出k個比x大的數就好了。數組

首先暴力去找左邊和右邊的數確定是不行的。咱們能夠經過set容器自動排序這一特性,實現這一想法。咱們知道x的左邊就意味着它的下標是比x的下標要小,同理,x的有邊就意味着它的下標是比x的下標要大,因此咱們只要把x的下標放入set中就能夠了。而後咱們在找 x 的左邊時要求每一次都可以找到下一個比當前x大的值。這樣就須要用數組用來存儲它的左邊和右邊的下一個比他大的位置。最後咱們在左邊統計出k個位置對應着右邊的k個位置,計算他們的位置差的乘積就能夠了。具體內容,請看代碼spa

AC代碼:code

 1 #include <iostream>
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 const int maxn=500005;
 5 int vis[maxn],left1[maxn],right1[maxn],nowl,nowr,nextl,nextr,l,r;
 6 int main()
 7 {
 8     int t,n,k;
 9     //freopen("1003.in","r",stdin);
10     //freopen("1003.out","w",stdout);
11     scanf("%d",&t);
12     while(t--)
13     {
14         scanf("%d%d",&n,&k);
15         int x;
16         for(int i=1;i<=n;i++)
17         {
18             scanf("%d",&x);
19             vis[x]=i;//處理數據也更方便索引
20             left1[i]=0;
21             right1[i]=n+1;
22         }
23         set<int>s;
24         set<int>::iterator ite;
25         left1[n+1]=0;
26         right1[n+1]=n+1;
27         s.insert(0);
28         s.insert(n+1);//默認第一個位置和最後一個位置上是最大值
29         long long ans=0;
30         for(int i=n;i>0;i--)
31         {
32             s.insert(vis[i]);//由大到小插入x
33             ite=s.find(vis[i]);//x插入的所在位置,它左邊的數都大於x且有ite-1個,對應序列中x左邊大於x的個數
34             ite++;
35             r=*ite;//右邊最靠近x的vis值
36             l=left1[r];//左邊最靠近x的vis值
37             left1[r]=vis[i];
38             right1[vis[i]]=r;
39             right1[l]=vis[i];
40             left1[vis[i]]=l;//把x插入l和r之間,這樣很方便索引
41             if(n-i+1<k)
42             continue;
43             nowl=vis[i];
44             for(int j=0;j<k&&nowl;j++)//左端找出k個比x大的數,有可能左端不足k個
45             nowl=left1[nowl];
46             nowr=nowl;
47             for(int j=0;j<k&&nowr!=n+1;j++)//從左端位置往右端找,湊足k個數
48             nowr=right1[nowr];
49             for(int j=0;j<k;j++)
50             {
51                 if(nowl==vis[i]||nowr==n+1)//k個數裏面必定要包含x,越過了x就能夠跳出了
52                 break;
53                 nextl=right1[nowl];
54                 nextr=right1[nowr];//這個位置到下一個位置之間是能夠任意選擇的,因此計算乘積就行了
55                 ans+=1ll*(nextl-nowl)*(nextr-nowr)*i;
56                 nowl=nextl;
57                 nowr=nextr;
58             }
59         }
60         printf("%lld\n",ans);
61     }
62     return 0;
63 }
相關文章
相關標籤/搜索