HDU 6058 Kanade's sum(链表)

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

题意:
找出所有区间第K大的值之和。

思路:

又有点贡献值的味道,就是考虑当前这个数贡献了几个区间。

然后往左和往右分别找大于当前数的k-1个数,这样就可以确定区间的个数,这要求我们从小到大找 并且找完之后需要删除这个数,用链表来维护。

删除元素的目的是为了加速,保证了当前查找的元素是最小值,所以只需要跳跃寻找k次就可以。具体看代码。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<sstream>
 6 #include<vector>
 7 #include<stack>
 8 #include<queue>
 9 #include<cmath>
10 #include<map>
11 #include<set>
12 using namespace std;
13 typedef long long ll;
14 typedef pair<int,int> pll;
15 const int INF = 0x3f3f3f3f;
16 const int maxn = 5*1e5 + 5;
17 
18 int n, k;
19 
20 int a[maxn];
21 int pre[maxn];
22 int nxt[maxn];
23 int pos[maxn];
24 
25 int before[maxn];
26 int after[maxn];
27 
28 ll solve(int p)
29 {
30     int c1=0,c2=0;
31     for(int i=p; i&&c1<=k; i=pre[i])
32     {
33         before[c1++]=i-pre[i];  //左边i个比当前数大的方法数
34     }
35     for(int i=p; i<=n&&c2<=k; i=nxt[i])
36     {
37         after[c2++]=nxt[i]-i;
38     }
39 
40     ll ans=0;
41     for(int i=0;i<c1;i++)  //左边i个,右边k-i+1个
42     {
43         if(k-i-1<c2)
44         {
45             ans+=before[i]*after[k-i-1];
46         }
47     }
48 
49     pre[nxt[p]]=pre[p];
50     nxt[pre[p]]=nxt[p];
51 
52     return ans;
53 }
54 
55 int main()
56 {
57     //freopen("in.txt","r",stdin);
58     int T;
59     scanf("%d",&T);
60     while(T--)
61     {
62         scanf("%d%d",&n,&k);
63         for(int i=1;i<=n;i++)
64         {
65             scanf("%d",&a[i]);
66             pos[a[i]]=i;
67         }
68         for(int i=1;i<=n;i++)
69         {
70             pre[i]=i-1;
71             nxt[i]=i+1;
72         }
73         pre[0]=0; nxt[n+1]=n+1;
74 
75         ll ans=0;
76         for(int i=1;i<=n;i++)
77         {
78             ans+=solve(pos[i])*i;
79         }
80         printf("%lld
",ans);
81     }
82     return 0;
83 }
原文地址:https://www.cnblogs.com/zyb993963526/p/7272993.html