传送门:cf1395D

题意

给定一个长度为n的数组a[i]为当天说话的有趣值,如果a[i]>m,那么在 i 之后有d天不能说话。否则可以每天都说话。找到一个排列使得n天有趣值总和最大,问有趣值总和的最大值是多少。

题解

很明显用贪心。先取>m的有趣值直到取不下。根据样例1的解释可以看出将一个大于m的值放在最后,前边放<m的会更优,所以此时有两种情况,如果取了的p个大于m的值p*(d+1)(p个大于m的总共占了多少天)大于n,那么就要找到最后一个能说话的天后不能说话的天数,即d-(p*(d+1)-n),然后可以加上<m最大的前d-(p*(d+1)-n)天,相当于把这段放在最后说话的那天前边。如果p*(d+1)<=n,剩下的位置就可以直接由大到小放<m的有趣值,因为最后一个>m的值后边也有d天不能说话,所以也将最后一个>m的值移到最后,相当于再加上d个<m的值,和前边的做法一样。然后就可以开始用<m的段替换>m的段,如果长度为d+1的<m的剩余的值和的最大值比所取的>m的值的最小值大,那么就可以直接用这一段替代>m的那一段,直到没有这样的段可以替代位置。

说的绕绕的,当时想的就绕绕的,代码还行,好像是有更简便的做法的。

代码

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 
 5 ll a[100100];
 6 ll b[100100];
 7 
 8 ll cmp(ll x,ll y)
 9 {
10     return x>y;
11 }
12 
13 int main()
14 {
15     ios::sync_with_stdio(false);
16     cin.tie(0);
17     cout.tie(0);
18     ll n,m,d;
19     cin>>n>>d>>m;
20     for(ll i=0;i<n;i++) cin>>a[i];
21     sort(a,a+n,cmp);
22     ll ans=0;
23     ll k=0,p=0,q=0;
24     for(ll i=0;i<n;i++){
25         if(a[i]<=m) b[k++]=a[i];
26     }
27     ll cnt=0;
28     for(ll i=0;i<n-k;i++){
29         if(cnt>=n) break;
30         cnt++;
31         ans+=a[i];
32         cnt+=d;
33         p++;
34     }
35     p--;
36     for(ll i=cnt;i<n;i++) ans+=b[q++];
37     if(k==0) {cout<<ans<<endl;return 0;}
38     if(cnt>n){
39         ll res=0;
40         ll pp=cnt-n;
41         pp=d-pp;
42         for(ll i=q;i<min(q+pp,k);i++) res+=b[i];
43         q+=pp;
44         ans+=res;
45     }
46     else{
47         ll res=0;
48         for(ll i=q;i<min(q+d,k);i++) res+=b[i];
49         q+=d;
50         ans+=res;
51     }
52     while(q<k&&p>=0){
53         ll res=0;
54         for(ll i=q;i<min(q+d+1,k);i++) res+=b[i];
55         if(res>a[p]) ans-=a[p],ans+=res,p--;
56         else break;
57         q+=d+1;
58     }
59     cout<<ans<<endl;
60     return 0;
61 }