MZOJ #81 最小得分和

分析

1.不能再暴力的暴力(40)

最暴力的方法:将所有的差求出来然后排序,选出最小的k个

复杂度:O (N2logN)

2.比较不暴力的暴力(80)

还记得蚯蚓吗,决策单调

这道题其实也有隐藏的单调,甚至比蚯蚓还要明显的多

先将n个数排序,当然,离i越近,差越小,这是相对i而言的

不过蚯蚓哪里只要三个队列就好,这里,如果用那个方法,怕是要n个队列

这道题还是用堆吧,看下面hty的题解中的80分做法

机房里也有大佬这么做

首先将每相邻两个元素差值入堆,每次选取当前堆中最小的数,找到它在原序列中的位置i,然后向左找到第一个没有取的数对(ip[i]),将堆顶元素变为s[i]-s[p[i]-1],s[i]为前缀和,然后decp[i]),做K次得到答案

复杂度:O(KlogN)

3.满分做法(100)

我考场上并没有想到80分的做法,直接由最暴力的做法得到了思路,于是写了正解……

然而我觉得80分的做法比正解要难想诶

 我们先看看暴力:

最暴力的方法:将所有的差求出来然后排序,选出最小的k个

对于每一个差,我们都可以找到一个减数,一个被减数

为了简化问题,我们将n个数从小到大排序,以免不必要的枚举

我们可以发现,对于每一个被减数,一定是离他近的减数与他相减所得的差小

有了以上分析的单调性,我们再看,对于每一个差值,差值越大,那么小于等于它的差值就越多

废话

对,就是这个性质,给了我们二分的可能

我们所求的就是前k个最小的差不是吗,这k个差有上限,也就是第k大的差值 

不如就二分这个上限,我们可以在O(n)的时间内求出这个上限所含的差值的个数:

求出了我们的上限,我们应该怎么算答案呢?

且看上面的程序,在求个数的时候,我们明显还维护了被减数的减数下限(也就是每一个i所对应的j)

a[i] 与a[j] ~ a[i-1]的差都小于等于第k大的差,都是要加到答案里的

重新组合整理一下,就变成了这样:

这个,完全可以用前缀和优化呀

于是,最终版是这样的:

Over

不对,你以为就这样完了?

太天真了

WA快乐

我们求出了第k大的值,可谁说小于等于第k大的差值就一定有k个呢

Σ(っ°Д°;)っ

看这组数据,

4 5

1 1 2 3

我们求出的差值为

0 1 1 1 2 2

第5大的是2,但是2有两个,上述代码没有处理多余的2

所以,代码的最后需要减去多余的2

cnt是实际小于等于第k大的差值的个数,cur是第k大的差值的值

真的Over了

复杂度:O(NlogN+NlogaN

4.题解 By hty(^v^)

题解当然要最后放……

代码

 1 /********************
 2 User:Mandy
 3 Language:c++
 4 Problem:
 5 ********************/
 6 #include<bits/stdc++.h>
 7 
 8 using namespace std;
 9 
10 typedef long long ll;
11 
12 const int maxn = 1e6 + 5;
13 
14 ll n,k;
15 ll a[maxn];
16 ll sum[maxn];
17 char *TT,*mo,but[(1 << 15) + 2];
18 #define getchar() ((TT == mo && (mo = ((TT = but) + fread(but,1,1 << 15,stdin)),TT == mo)) ? -1 : *TT++) 
19 template<class T>inline void read(T &x){
20     x = 0;bool flag = 0;char ch = getchar();
21     while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
22     while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
23     if(flag) x = -x;
24 }
25 
26 template<class T>void putch(const T x){
27     if(x > 9) putch(x / 10);
28     putchar(x % 10 | 48);
29 }
30 
31 template<class T>void put(const T x){
32     if(x < 0) putchar('-'),putch(-x);
33     else putch(x);
34 }
35 
36 void file(){
37     freopen("mark.in","r",stdin);
38     freopen("mark.out","w",stdout);
39 }
40 
41 void readdata(){
42     read(n);read(k);
43     for(int i = 1;i <= n; ++ i) read(a[i]);
44     sort(a + 1,a + 1 + n);
45 }
46 
47 bool check(ll x){
48     ll j = 1,cnt = 0;
49     for(ll i = 2;i <= n; ++ i){//被减数 
50         
51         while(a[i] - a[j] > x)//减数 
52             j++;
53         cnt += i - j;
54         if(cnt >= k) return 1;
55     }
56     return 0;
57 }
58 
59 void work(){
60     long long l = 0,r = a[n] - a[1],cur;
61     while(l <= r){//二分第k大的差 
62         ll mid = (l + r) >> 1;
63         if(check(mid)) cur = mid,r = mid - 1;
64         else l = mid + 1;
65     } 
66     ll j = 1;
67     for(int i = 1;i <= n; ++ i) sum[i] = sum[i - 1] + a[i];
68     ll cnt = 0,ans = 0;
69     for(ll i = 2; i <= n ; ++ i){
70         while(a[i] - a[j] > cur) j++;
71         ans = ans - (sum[i-1] - sum[j-1]) + a[i] * (i-j);
72         cnt += (long long)i-j; 
73     }
74     if(cnt > k) ans -= cur * (cnt - k);
75     put(ans);
76 }
77 
78 int main(){
79 //    file();
80     readdata();
81     work();
82     return 0;
83 }
View Code

 

posted @ 2019-09-06 19:37  Mandy_H_Y  阅读(161)  评论(0编辑  收藏  举报