Desription 给你n个数。现在你可以选择若干个数(也可以一个都不选),选择的价值为所有数的价值和。现在给所有可能的选择,先按权值从小到大排序,对于权值相同的,根据所用数字集合的标号的字典序从小到大排序。请输出第k小的选择的价值,以及所用的选择集合。
Sample Input 4 10 3 7 4 3
Sample Output 10 1 3 4
这题卡长卡了我一上午。。。 首先我们如果按照DFS的思想,每一次对于一个数,我们要么选,要么不选,但这样是肯定会T的,我们需要一些想法。 如果我希望使序列长度加1,那我肯定是选择前面一个最小的数。 如果我们使序列长度增长,那我们肯定就使选择一个当前的次小值将他替换当前队尾的值,这里你可以用超级钢琴的思想来做,然后用个堆维护。 然后关于字典序之类的,你可以用他pre的rank来瞎搞搞。 (关于卡长,有一个小技巧:把数组维数换一换就能快。。。 我本来st表是这么开的:int f[1000010][21] 给成这样就快了:int f[21][1000010] 至于能快多少,这道题里面,差不多有6,7s这样子。。。 )
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std
;
typedef long long LL
;
inline int read() {
int s
= 0, f
= 1; char ch
= getchar();
while(ch
< '0' || ch
> '9') {if(ch
== '-') f
= -1; ch
= getchar();}
while(ch
>= '0' && ch
<= '9') s
= s
* 10 + ch
- '0', ch
= getchar();
return s
* f
;
}
struct node
{
LL sum
;
int l
, r
, x
, pre
, rank
;
} p
[3000010];
int o
, heap
[3000010];
LL a
[1000010];
int Log
[1000010], f
[21][1000010], bin
[21];
inline int _min(int x
, int y
) {return a
[x
] <= a
[y
] ? x
: y
;}
inline bool pd(int x
, int y
) {
if(p
[x
].sum
== p
[y
].sum
) {
if(p
[x
].x
== p
[y
].x
) return p
[p
[x
].pre
].rank
< p
[p
[y
].pre
].rank
;
return p
[x
].x
< p
[y
].x
;
} return p
[x
].sum
< p
[y
].sum
;
}
inline void up(int x
) {
while(x
) {
if(x
/ 2 && pd(heap
[x
], heap
[x
/ 2])) swap(heap
[x
], heap
[x
/ 2]), x
/= 2;
else break;
}
}
inline void down(int x
) {
int s
= x
* 2;
while(s
<= o
) {
if(s
< o
&& pd(heap
[s
+ 1], heap
[s
])) s
++;
if(pd(heap
[s
], heap
[x
])) swap(heap
[x
], heap
[s
]), x
= s
, s
*= 2;
else break;
}
}
inline int query(int l
, int r
) {
register int hh
= Log
[r
- l
+ 1];
return _min(f
[hh
][l
], f
[hh
][r
- bin
[hh
] + 1]);
}
inline void print(int x
) {
printf("%d ", p
[x
].x
);
if(p
[x
].pre
) print(p
[x
].pre
);
}
int main() {
int n
= read(), k
= read(); k
--;
if(k
== 0) {
printf("0\n");
return 0;
}
a
[0] = 999999999;
register int i
, j
;
for(i
= 2; i
<= n
; ++i
) Log
[i
] = Log
[i
>> 1] + 1;
for(i
= 1; i
<= n
; ++i
) a
[i
] = read(), f
[0][i
] = i
;
bin
[0]=1;
for(i
= 1; i
<= 20; ++i
) bin
[i
] = bin
[i
- 1] << 1;
for(i
= 1; bin
[i
] <= n
; ++i
) for(j
= 1; j
+ bin
[i
- 1] <= n
; ++j
) f
[i
][j
] = _min(f
[i
- 1][j
], f
[i
- 1][j
+ bin
[i
- 1]]);
int tp
= 1;
p
[1].l
= 1, p
[1].r
= n
, p
[1].x
= query(1, n
), p
[1].sum
= a
[p
[1].x
], p
[1].pre
= 0;
heap
[o
= 1] = 1;
LL last
= -1, hh
= 0;
while(k
--) {
int g
= heap
[1];
swap(heap
[1], heap
[o
--]);
down(1);
node now
= p
[g
];
if(!k
) {printf("%lld\n", now
.sum
); print(g
); return 0;}
if(now
.sum
> last
) last
= now
.sum
, hh
= 1;
else hh
++;
p
[g
].rank
= hh
;
if(now
.x
> 1) {
int j
= ++tp
;
p
[j
].l
= 1, p
[j
].r
= now
.x
- 1, p
[j
].x
= query(1, now
.x
- 1);
p
[j
].sum
= now
.sum
+ a
[p
[j
].x
], p
[j
].pre
= g
;
heap
[++o
] = j
; up(o
);
}
if(now
.l
< now
.x
) {
int j
= ++tp
;
p
[j
].l
= now
.l
, p
[j
].r
= now
.x
- 1, p
[j
].x
= query(now
.l
, now
.x
- 1);
p
[j
].sum
= now
.sum
+ a
[p
[j
].x
] - a
[now
.x
], p
[j
].pre
= now
.pre
;
heap
[++o
] = j
; up(o
);
}
if(now
.x
< now
.r
) {
int j
= ++tp
;
p
[j
].l
= now
.x
+ 1, p
[j
].r
= now
.r
, p
[j
].x
= query(now
.x
+ 1, now
.r
);
p
[j
].sum
= now
.sum
+ a
[p
[j
].x
] - a
[now
.x
], p
[j
].pre
= now
.pre
;
heap
[++o
] = j
; up(o
);
}
}
return 0;
}