题目链接:https://codeforces.com/contest/1282/problem/C
题目大意:
有n道题,一共T的答题时间,简单题需要时间a,难题需要b,每道题都有ddl,如果实际答题时间到达ti,那么这道题就必须要做,否则只能拿零分,每题一分,问最多能拿几分
题目思路:
日常被C题卡。。。哭了。我刚开始想简单了,就直接双指针,如果做了一个题,其他题ddl到了,就一排全部带走,否则尽量做简单题。反例太多了。。。。。后来又看了题解。。。菜!强行总结出这种题技巧,就是只要出现过了某个时间点就会变性质的题目,那么这个时间点的附近时间点可能就是题目的关键!
不卖关子了,首先我们可以发现,对于每个ddl的前一秒,如果能在这个时间点完成必做任务,而且没有超时,那么这个时间点就不会产生关联到其他题目的关键时间点。这个时间点的好处在于,把动态的过程(如果时间超过了某一题ddl,这题变成必做会影响结束时间)变成了静态(不再有题目变成必做,所以就是不用怕会有新的必做题出现)。这道题的做法就是,搞出所有题目ddl-1再加上T也就是考试结束时间,这些作为关键点,排序去重。然后判断每个时间点能够得到的分数。怎么判断呢?首先先看这个时间段有多少必做简单题数量
c
1
c1
c1和必做难题数量
c
2
c2
c2,直接用while即可,只要ddl小于等于正在枚举的时间点
t
t
t,那就是必做题,那么剩余时间就是
t
−
a
∗
c
1
−
b
∗
c
2
t-a*c1-b*c2
t−a∗c1−b∗c2,如果剩余时间是负数,就说明这个时间点不够完成必做任务直接歇逼,如果还有剩,那么聪明的大家伙肯定想到了,可以用来不是必做的题,由于简单题需要的时间少,所以先做简单题,
m
i
n
(
r
s
t
/
a
,
p
p
1
−
c
1
)
min(rst/a,pp1-c1)
min(rst/a,pp1−c1) 表示还能做多少简单题,要么是时间不够了,要么是题不够了,难题也一样,然后就能得到这个时间点一共能做的题,取max这题就结束战斗了。
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
const int MAXN = 2e5+5;
const int MOD = 10007;
int x[MAXN],y[MAXN],p[MAXN];
int h[MAXN],k[MAXN];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,T,a,b;
scanf("%d%d%d%d",&n,&T,&a,&b);
rep(i,1,n){
scanf("%d",&x[i]);
}
int pos=0;
int pp1=0,pp2=0;
rep(i,1,n){
scanf("%d",&y[i]);
if(x[i])k[++pp2]=y[i];
else h[++pp1]=y[i];
p[++pos]=y[i]-1;
}
p[++pos]=T;
sort(p+1,p+pos+1);
sort(k+1,k+pp2+1);
sort(h+1,h+pp1+1);
pos=unique(p+1,p+pos+1)-p-1;
ll ans=0;
int c1=0,c2=0;
h[pp1+1]=1e9+7;
k[pp2+1]=1e9+7;
rep(i,1,pos){
while(h[c1+1]<=p[i])c1++;
while(k[c2+1]<=p[i])c2++;
ll temp=c1+c2;
ll rst=p[i]-(ll)c1*a-(ll)c2*b;
if(rst<0)continue;
int num=min(rst/a,(ll)pp1-c1);
temp+=num;
rst-=num*a;
num=min(rst/b,(ll)pp2-c2);
temp+=num;
ans=max(ans,temp);
}
printf("%I64d\n",ans);
}
}