3647. 【GDOI2014】oj
Description
小M是一个勤奋的ACMer,他利用课余时间刷了很多题目。但他是个很健忘的孩子,经常会忘记自己刷过一些什么题目,所以他想写一个OJ来管理自己做过的题目。
经过一个星期的努力,小M的OJ基本成型,只是还差一个Contest的模块没有实现。小M觉得这个模块很难实现,所以他希望找你来帮忙。
小M告诉你,一个OJ的基础元素包括:
1、题目,可以用pid唯一标识,pid为正整数。
2、比赛,可以用cid唯一标识,cid为正整数。
3、用户,可以用uid唯一标识,uid为正整数。
4、提交状态,可以由sid唯一标识,sid为正整数。一个提交状态是由sid, cid, pid, uid, result组成的,分别表示本条状态的提交ID,所属比赛ID,题目ID,用户ID以及评测结果。
简单起见,这里的result只有AC,UNAC,WAIT三种状态,分别表示通过,不通过,等待评测。
同时小M提出一个比赛模块需要实现以下请求:
1.createContest cid t pid_1 pid_2 ... pid_t
表示要创建一个比赛,cid是一个正整数,是这场比赛的唯一标识。
t表示这场比赛有t(1<=t<=1000)道题目,接下来t个不同的整数,表示这场比赛的题目编号。
2.submission sid cid pid uid result
该条状态的sid要么之前没出现过,要么以前出现过,但是被rejudge了。
result为AC或者UNAC。
3.getRank cid uid
在一场比赛中,所有有提交的用户都应该算在排名内(包括被rejudge的提交),用户的排名按照通过的题目数从大到小排序,如果题目数相同,则按随机顺序排序。
该指令需要统计用户uid在cid这场比赛中的通过目数,最高排名以及最低排名。
值得注意的是,用户uid在cid这场比赛中同一道题目的多个通过记录只算一次。
输出格式为: uid solved highest lowest
分别代表用户ID,通过题目数量,最高排名以及最低排名,其中highest <= lowest。
4.rejudge sid
重测以sid标识的提交记录,即将该记录的result改成WAIT
Solution
题目很长,简单来说就是维护一个比赛系统。
先给出要维护的东西:
每场比赛每个用户对应的 AC 数量,每场比赛对应 AC 数量的人数,每个提交对应的编号、用户、比赛、和结果。
下面分操作讲述。
新建比赛。
其实新建比赛是没有用的,因为之后其他的操作中都会给出对应的比赛编号和题目编号(或者提交编号),所以并不需要记录比赛中有哪些题目。
提交。
对于一个 UNAC 的提交,记录每场比赛有哪些人是交过题了,如果当前这个提交对应的用户之前交过题,那么这个提交是不用管的,否则需要将 AC 数量为 0 的人数 +1(在对应的比赛中)。
如果是 AC 的提交,则将对应用户在对应比赛中的 AC 数量 +1。注意我们要先判断这个人是否曾经 AC 过这道题目,但如果直接开数组会爆空间,所以要用链式前向星,将对于这个用户和这道题对应比赛的 AC 数量打在连向对应比赛的边上(这里可能有点难懂,可以结合下面的代码理解)。
查询排名。
相当于给出一个过题数,在对应比赛中查找有多少人的过题数大于,多少人过题数等于。这里可以用数据结构或者前缀/后缀和。
重测。
跟提交差不多,如果原本的状态是 UNAC 的话,不需要管。
如果是 AC,则只需要将对应用户的 AC 数量 -1。
Code
#include<cstdio>
#define N 5005
#define M 350005
using namespace std;
struct sub
{
int pid,cid,uid;
bool res;
}sub[M];
struct contest
{
int num[N],solve[N];
}con[55];
struct node
{
int to,next;
}a[M];
int n,m,q,id,sid,cid,pid,uid,tot,cnt[M],head[N][N];
bool b[55][N];
char ch[20];
int read()
{
int res=0;char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch-'0'),ch=getchar();
return res;
}
void add(int uid,int pid,int cid)
{
a[++tot].to=cid;
a[tot].next=head[uid][pid];
head[uid][pid]=tot;
}
int find(int uid,int pid,int cid)
{
for (int i=head[uid][pid];i;i=a[i].next)
{
int v=a[i].to;
if (v==cid) return i;
}
return 0;
}
int main()
{
n=read();m=read();q=read();
while (q--)
{
scanf("%s",ch);
if (ch[0]=='c')
{
int num,x;
cid=read();num=read();
for (int i=1;i<=num;++i)
x=read();
}
else if (ch[0]=='s')
{
int sid,cid,pid,uid;
sid=read();cid=read();pid=read();uid=read();scanf("%s",ch);
sub[sid].cid=cid;sub[sid].pid=pid;sub[sid].uid=uid;sub[sid].res=(ch[0]=='A');
if (!b[cid][uid])
{
b[cid][uid]=true;
con[cid].num[0]++;
}
if (ch[0]=='A')
{
id=find(uid,pid,cid);
if (!id) add(uid,pid,cid),id=tot;
++cnt[id];
if (cnt[id]==1) con[cid].num[++con[cid].solve[uid]]++;
}
}
else if (ch[0]=='g')
{
cid=read();uid=read();
int pnum=con[cid].solve[uid];
printf("%d %d %d %d\n",uid,pnum,con[cid].num[pnum+1]+1,con[cid].num[pnum]);
}
else if (ch[0]=='r')
{
sid=read();
uid=sub[sid].uid;cid=sub[sid].cid;pid=sub[sid].pid;
if (sub[sid].res)
{
int id=find(uid,pid,cid);
--cnt[id];
if (cnt[id]==0) con[cid].num[con[cid].solve[uid]--]--;
}
}
}
return 0;
}