[hdu7182]Fall with Full Star

将所有关卡分为$d_{i}\ge 0$和$d_{i}<0$两类,显然优先选前者,即两部分独立

在此基础上,将后者的过程倒序并简单处理,即可转化为与前者相同的子问题

关于子问题,考虑钦定贡献关卡,显然优先选其余关卡,且"钦定关卡"按$s_{i}-d_{i}$从小到大

将所有关卡按$s_{i}-d_{i}$从小到大排序,有以下两种做法——

做法1-dp

定义$f_{i,j}$表示$[i,n]$中钦定$j$个关卡时最小的$\sum_{钦定i}d_{i}$,转移即
$$
f_{i,j}=\min\left(f_{i+1,j},\begin{cases}f_{i+1,j-1}+d_{i}&f_{i+1,j-1}\le s_{0}+\sum d_{i}-s_{i}\\\infty &f_{i+1,j-1}>s_{0}+\sum d_{i}-s_{i}\end{cases}\right)
$$
显然$f_{i,j}$关于$j$单调不降,且具有凸性(即$\Delta f_{i,j}=f_{i,j+1}-f_{i,j}$同样单调不降)

此时,取$j=\max_{f_{i+1,j_{0}}\le s_{0}+\sum d_{i}-s_{i}}j_{0}$,转移也即在$\Delta f_{i+1,[0,j]}$中插入$d_{i}$并删除最大值

(另外,关于凸性的证明,结合上述过程归纳即可)

使用平衡树维护,时间复杂度为$o(n\log n)$,可以通过

做法2-贪心

从后往前,维护当前最优解(及对应方案),并分类讨论:

1.若能钦定$i$,则直接钦定即可

2.若不能钦定$i$且删除最优解中$d_{i}$最大的位置后可以钦定$i$,则删除后钦定

3.若均不能,则不钦定$i$,直接跳过即可

(贪心的正确性不太会证QAQ)

用set维护选中的$d_{i}$即可,时间复杂度也为$o(n\log n)$,可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 100005
 4 #define ll long long
 5 int t,n,na,nb,x,ans;ll s,y;
 6 multiset<int>S;
 7 struct Data{
 8     int d;ll s;
 9     bool operator < (const Data &n)const{
10         return s-d<n.s-n.d;
11     }
12 }a[N],b[N];
13 void calc(int n,ll s,Data *a){
14     sort(a+1,a+n+1);
15     for(int i=1;i<=n;i++)s+=a[i].d;
16     ll sum=0;S.clear();
17     for(int i=n;i;i--){
18         if (sum<=s-a[i].s)sum+=a[i].d,S.insert(a[i].d);
19         else{
20             if (S.empty())continue;
21             int k=(*--S.end());
22             if (sum-k<=s-a[i].s){
23                 sum+=a[i].d-k;
24                 S.erase(--S.end()),S.insert(a[i].d);
25             }
26         }
27     }
28     ans+=S.size();
29 }
30 int main(){
31     scanf("%d",&t);
32     while (t--){
33         scanf("%d%lld",&n,&s);
34         na=nb=ans=0;
35         for(int i=1;i<=n;i++){
36             scanf("%d%lld",&x,&y);
37             if (x>=0)a[++na]=Data{x,y};
38             else b[++nb]=Data{-x,y-x};
39         }
40         calc(na,s,a);
41         for(int i=1;i<=na;i++)s+=a[i].d;
42         for(int i=1;i<=nb;i++)s-=b[i].d;
43         calc(nb,s,b),printf("%d\n",ans);
44     }
45     return 0;
46 }
View Code

 

posted @ 2022-08-05 15:13  PYWBKTDA  阅读(91)  评论(0编辑  收藏  举报