题目传送门(内部题134)
输入格式
第一行两个数$N,L$。
接下来$N$行每行两个数$A_i,B_i$。
接下来$N$行每行一个整数$C_i$。
输出格式
一行一个整数表示答案,无解输出$-1$。
样例
样例输入1:
3 9
6 3
5 2
3 1
2
2
2
样例输出1:
2
样例输入2:
5 20
3 2
4 2
6 3
8 4
10 5
4
2
3
4
5
样例输出2:
-1
数据范围与提示
对于$40\%$的数据,$N\leqslant 1,000$;
对于额外$20\%$的数据,$B_i=0$;
对于额外$20\%$的数据,$C_i=0$;
对于$100\%$的数据,$n\leqslant 10^5,0\leqslant A_i,B_i,C_i\leqslant 10^9$。
题解
瞎打都能过的题……
考场打了个假贪心$A$掉了,然而打部分分的$KX$表示不服(因为数据中没有给部分分),于是他就跟教练要求要出数据,于是……
上面的$KX$有他博客的链接,欢迎大家消费$+$吐槽。
考场上的贪心实际上已经很接近正解了。
首先,按$A_i-B_i$排序,在爬不上去的情况下一定是优先选择$A_i-B_i$最大的。
然后发现其实有的时候可以用一个$A_i$很大的“冲”上去,所以开一个大根堆就好了。
但是还有一个情况,我们可以用一些$A_i-B_i$比较小的“积蓄能量”,然后用一个$A_i-B_i$较大但是$A_i$更大的“冲”上去。
然后发现只与最后一个有关,所以可以只考虑最后一个选什么,用倍增思想维护即可。
时间复杂度:$\Theta(n\log n)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h> using namespace std; struct rec{int A,B,delta;}e[100001]; int N; long long L; long long C[100001],S[100001],F[20][100001]; int lg[100001]; int limits; bool vis[100001]; int ans=0x3f3f3f3f; bool cmp(rec a,rec b){return a.delta>b.delta;} int cal(int l,int r){return min(F[lg[r-l+1]][l],F[lg[r-l+1]][r-(1<<lg[r-l+1])+1]);} int main() { scanf("%d%lld",&N,&L);vis[0]=1; for(int i=2;i<=N;i++)lg[i]=lg[i>>1]+1; for(int i=1;i<=N;i++) { scanf("%d%d",&e[i].A,&e[i].B); e[i].delta=e[i].A-e[i].B; } sort(e+1,e+N+1,cmp);limits=N; for(int i=1;i<=N;i++)if(e[i].delta<0){limits=i-1;break;} for(int i=1;i<=N;i++) { scanf("%d",&C[i]); C[i]+=C[i-1]; S[i]=S[i-1]+e[i].delta; F[0][i]=S[i]+e[i].delta-C[i]; if(vis[i-1]&&C[i]<S[i])vis[i]=1; } for(int i=1;i<=lg[N];i++) for(int j=1,s=(1<<(i-1));j<=N-(1<<j)+1;j++) F[i][j]=min(F[i-1][j],F[i-1][j+s]); long long now; int pos; for(int i=1;i<N;i++) { now=L+e[i].delta-e[i].A; pos=lower_bound(S+1,S+limits+1,now)-S; if(pos==limits+1)continue; if(vis[i-1]&&pos>i) if(cal(i,pos)>e[i].delta) ans=min(ans,pos); } for(int i=N,mx=0;i>=0;i--) { if(vis[i]&&S[i]+mx>=L) ans=min(ans,i+1); mx=max(mx,e[i].A); } if(ans==0x3f3f3f3f)puts("-1"); else printf("%d\n",ans); return 0; }
rp++
原文地址:https://www.cnblogs.com/wzc521/p/11824354.html
时间: 2024-11-08 03:51:58