[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 }