题解 CF732D 【Exams】

最后无良宣传一下博客 \(wwwwww\)
文章列表 - 地灵殿 - 洛谷博客


知识点: 贪心, 二分答案, 模拟

原题面

分析题意:

可以发现 , 复习的科目种类是没有影响的
可根据考试的不同 调整复习课目
只需要考虑复习天数是否足够即可

若只考虑 能否完成所有考试:

  • 显然, 在一次考试最后一次出现时将其完成必然最优
    因为这样有 最多的复习时间

    若在上述情况下仍然无法将所有考试完成, 则必不合法

由于答案满足单调性, 则可考虑二分答案 :

  • 检查合法性时, 按照上述贪心策略,
    在每一次考试 在二分量之前的最后一次出现位置 完成,
    其余时间 都用来复习, 必然更优

    检查在考试天时, 复习天数是否足够完成考试即可

总复杂度 \(O(n\log n)\)


\(AC\) 代码

#include <cstdio>
#include <cstring>
#include <ctype.h>
#define int long long
const int MARX = 1e5 + 10;
//=============================================================
int N, M, first, ans, d[MARX], a[MARX], last[MARX], checker[MARX];
//=============================================================
inline int read()
{
    int s = 1, w = 0; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') s = -1;
    for(; isdigit(ch); ch = getchar()) w = (w << 1) + (w << 3) + (ch ^ '0');
    return s * w;
}
bool check(int lim)//检查合法性 
{
	memset(checker, 0, sizeof(checker));
	for(int i = 1; i <= lim; i ++) last[d[i]] = i;//记录最后一次出现位置 
	for(int i = 1; i <= M; i ++) checker[last[i]] = a[i];//对进行考试的天进行标记 
	
	for(int i = 1, sum = 0; i <= lim; i ++)
	{
	  if(sum < checker[i]) return 0;//不合法 
	  if(! checker[i]) sum ++;//第i天复习 
	  else sum -= checker[i];//第i天考试 
	}
	return 1;
}
//=============================================================
signed main()
{
	N = read(), M = read();
	for(int i = 1; i <= N; i ++)  
	{
	  d[i] = read();
	  if(! last[d[i]]) first = i;
	  last[d[i]] = i;
	}
	for(int i = 1; i <= M; i ++)  
	{
	  a[i] = read();
	  //检查是否有一科考试, 在N天中都没有出现: 
	  if(! last[i]) {printf("-1"); return 0;} 
	}
	
	if(! check(N)) {printf("-1"); return 0;}//检查是否能完成 
	for(int l = first, r = N; l <= r;)//二分答案 
	{
	  int mid = (l + r) >> 1;
	  if(check(mid)) ans = mid, r = mid - 1;//检查, 调整边界 
	  else l = mid + 1;
	}
	printf("%lld", ans);
}
posted @ 2019-11-08 17:07  Luckyblock  阅读(137)  评论(0)    收藏  举报