【ICPC】2022济南站 D. Frozen Scoreboard | 搜索、贪心、阅读理解

530 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情

题目链接

D (codeforces.com)

题目大意

原题目太长就不粘了……

题目大意是这样的:

一场比赛里有 nn 支参赛队伍,有 mm 道题,比赛共 300300 min,其中最后 6060 min 会封榜。封榜后,每个队伍的做题情况由 mm 行信息表示,第 ii 行信息表示该队伍第 ii 题的情况,有以下四种:

  • . 该队伍没有提交第 ii 题。
  • + x/y 该队伍对第 ii 题进行了至少 xx 次提交,前 xx 次提交均没有通过,第 xx 次提交通过了,提交时间为比赛开始后的第 yy min。
  • - x 该队伍对第 ii 题进行了 xx 次提交,均没有通过。
  • ? x y 该队伍对第 ii 题共进行了 yy 次提交,封榜前的提交均没有通过。封榜后进行了 xx 次提交,是否通过、第几次提交通过、什么时候通过均未知。

比赛以通过题的数量从大到小为第一关键字、罚时从小到大为第二关键字进行排名。每个队伍的罚时是该队伍每道题的罚时之和,对于该队伍第 ii 道题的罚时计算规则如下:

  • 如果该没有通过第 ii 道题,罚时为 00
  • 如果该队伍通过了第 ii 道题,是在第 xx 次提交时通过的,且第 xx 次提交在 比赛开始后的第 yy min,那么这道题的罚时是 20×(x1)+y20\times(x-1)+y

对于第 ii 支队伍,预测其最终成绩为 ai,bia_i,b_i 表示队伍 ii 最终通过了 aia_i 道题,罚时是 bib_i。对于给出的封榜后的榜单,判断该队伍是否可能取得预测的最终成绩,若有可能,构造任意一组合法解。

同一 min 可能进行无限次提交,正确提交后的提交不计算罚时。

思路

感觉做法很多。

首先每个队伍相互独立,视为 nn 组数据。只考虑当前队伍……

先按题意统计已经确定的过题数量和罚时。作差后,假设我们封榜后需要通过 acac 道题,罚时为 ftft 才能契合预测结果。对于封榜后的提交我们压入数组 bb 中。

因为题目数量很小,我们可以直接暴力 bb 数组中的每个题通过与否。我们枚举的 acac 道题最小罚时是每道题都在 240240 min 的封榜后第一次提交就通过了;最大罚时是每道题都在最后一次提交且在比赛结束前最后一分钟才通过。容易发现这个范围内的所有罚时都可以取到,若 ftft 落在区间内,给本次选择的题目打上标记。对于所有可能的选择如果都不能使得 ftft 落在罚时范围内,就输出 No

现在我们处理 Yes 时的输出方案。即把罚时分配给判断时打上标记的每个题。

我的策略是先给每个题分配最小的满足条件的罚时,即 240240 加上其封榜前提交的罚时。然后再遍历一遍带标记的题,尽可能多的给每个题分配罚时,尽可能让罚时来自于错误的提交。(怎么组织语言都乱七八糟的,详见代码 QAQ)

代码

#include <iostream>
#include <algorithm>
#include <math.h>
#include <stdio.h>
#include <map>
#include <vector>
#include <queue>
#define nnn printf("No\n")
#define yyy printf("Yes\n")
using namespace std;
using LL=long long;
const int N=15;
const LL mod=1000000007;
//const LL mod=998244353;
struct asdf{
	int x,y,id;
	char opt;
}a[N],b[N];
int n,m,ft,ac,tot,use[N];
char ch;
bool dfs(int t,int num,int l,int r)
{
	if (num==ac)
	{
		if (!((l<=ft)&&(ft<=r))) return 0;
		while (t<=tot) use[t++]=0;
		return 1;
	}
	if (num>ac) return 0;
	if (tot-t+1+num<ac) return 0;
	use[t]=1;
	if (dfs(t+1,num+1,l+(b[t].y-b[t].x)*20+240,r+(b[t].y-1)*20+299)) return 1;
	use[t]=0;
	return dfs(t+1,num,l,r); 
}
bool solve()
{
	if (ac<0||ft<0||ac>tot) return 0;
	return (dfs(1,0,0,0));
}
void print()
{
	yyy;
	for (int i=1;i<=tot;++i)
		if (use[i])
		{
			a[b[i].id].opt='+';
			a[b[i].id].y=(b[i].y-b[i].x)*20+240;
			ft-=(b[i].y-b[i].x)*20+240;
		}
		else a[b[i].id].opt='-',a[b[i].id].x=b[i].y;
	for (int i=1;i<=tot;++i)
	{
		if (!use[i]) continue;
		a[b[i].id].y+=min(ft,(b[i].x-1)*20+59);
		ft-=min(ft,(b[i].x-1)*20+59);
		a[b[i].id].x=min(((a[b[i].id].y-240)/20),b[i].y-1)+1;
		a[b[i].id].y-=(a[b[i].id].x-1)*20;
	}
	for (int i=1;i<=m;++i)
	{
		printf("%c ",a[i].opt);
		if (a[i].opt=='-') printf("%d",a[i].x);
		if (a[i].opt=='+') printf("%d/%d",a[i].x,a[i].y);
		printf("\n");
	}		

}
int main()
{
	scanf("%d",&n);
	scanf("%d",&m);
	while (n--)
	{
		tot=0;
		scanf("%d%d",&ac,&ft);
		for (int i=1;i<=m;++i)
		{
			cin>>a[i].opt;
			a[i].id=i;
			if (a[i].opt=='+')
			{
				scanf("%d/%d",&a[i].x,&a[i].y);
				ac--;
				ft-=a[i].y;
				ft-=(a[i].x-1)*20;
			}
			if (a[i].opt=='?') scanf("%d%d",&a[i].x,&a[i].y),b[++tot]=a[i];
			if (a[i].opt=='-') scanf("%d",&a[i].x);
		}
		if (solve()) print();
		else nnn;
	}
	return 0;
}