HDU 6058

或许这就是弱渣吧,全世界的都会的题就我不会——链表


题意

求全排列所有区间的第k大的权值和,不存在第k大权值为0.

  • n<=5*10^5
  • k<=80

题解

  • 考虑链表实现,按输入数组的顺序建一个链表,再用指针数组保存每个元素的地址
  • 从小到大枚举每个元素的贡献
  • 对当前元素,定位到链表位置,把前面k个和后面K个按序保存下来,然后算贡献就行了

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
#define maxn 500010
bool scan_d(int &ret) {
char c;
int sgn;
if(c=getchar(),c==EOF) return 0; //EOF
while(c!='-'&&(c<'0'||c>'9')) c=getchar();
sgn=(c=='-')?-1:1;
ret=(c=='-')?0:(c-'0');
while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
ret*=sgn;
return 1;
}
struct node {
int x;
node *left,*right;
node() {}
node(int x,node *left,node *right):x(x),left(left),right(right) {}
};
int A[maxn];
node *root;
node *pos[maxn];
void init(int n) {
node *p=root=new node();
A[n+1]=n+1;
for(int i=0; i<=n+1; i++) {
node *t=new node(i,p,NULL);
p->right=t;
pos[A[i]]=p=t;
}
}
int t[250];
int main() {
int ca;
scanf("%d",&ca);
while(ca--) {
int n,k;
scan_d(n);
scan_d(k);
for(int i=1; i<=n; i++)
scan_d(A[i]);
init(n);
ll ans=0;
for(int i=1; i<=n; i++) {
node *p=pos[i];
int cnt=k,len=0;
while(cnt--) {
p=p->left;
if(p->x==0) break;
}
if(cnt==-1) cnt=0;
cnt=k-cnt+1;
while(cnt--) {
t[len++]=p->x;
p=p->right;
}
cnt=k;
while(cnt--) {
t[len++]=p->x;
p=p->right;
if(!p) break;
}
p=pos[i];
p->left->right=p->right;
p->right->left=p->left;
delete p;
for(int j=1; j<len; j++) {
if(j+k>=len) break;
ans+=(ll)i*(t[j]-t[j-1])*(t[j+k]-t[j+k-1]);
}
}
printf("%lld\n",ans);
while(root) {
node *p=root;
root=root->right;
delete p;
}
}
return 0;
}