【TJOI2019】【平衡树】甲苯先生的滚榜

【描述】
甲苯先生在制作一个online judge,他发现做比赛的人们很关心自己的排名(显而易见),在acm赛制的比赛中,如果通过题目数量不相等,则通过题目数量多的人排名更靠前,如果通过题目数量相等,则罚时更少的人排名更高。甲苯先生想让大家帮忙设计一个程序,每次有人通过之后,就告诉他排名在他的前面有多少人。(不包括和他罚时题数都相同的同学)

【输入】
第一行输入一个整数T表示样例数。对于每一个样例:输入三个整数m, n, seed。m表示参赛总人数(编号1−m),n表示一共有n次accept(假设accept已经去重,即不存在相同人的相同题目提交)。seed表示生成数据的种子。

接下来要求同学们使用之下的函数生成数据

typedef unsigned int ui;
    ui randNum(ui& seed, ui last, const ui m) {
    seed = seed * 17 + last;
    return seed % m + 1;
}

(last为上一次输出的结果,在没有输出结果时last=7)

要求每次生成两个数据Ria,Rib 表示Ria的人Accept了一道题目,他的罚时为Rib。(也就是说Ria的题目数量+1,罚时长度+Rib)。

要求一共生成n组数据,代表一共有n次提交

对于所有数据,保证罚时总和不超过1500000
输出
每次提交输出一行整数,表示Ria在ac后比Ria成绩高的有多少选手。

【样例输入】
1
7 3 1
【样例输出】
0
1
0

【思路】

貌似没什么好讲的,这就是一个模板,用平衡树维护一个二元组。用pair或者结构体重载运算符即可。值得一提的是,操作过程中,我们不关心节点与人的对应,对于若干个相同的节点,我们任意取出一个进行操作即可。也就是说,平衡树并不是维护每个人的信息,只是维护这样的一种局面。另外,如果没有O2,这道题有点卡常,可能需要对读入的函数进行一些丧心病狂 的修改。最初建树时注意清空。对于这种多组数据题:数组不清空,爆零两行泪

#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=1e5+5;
typedef unsigned int ui;
int a,b,l,r,p,n,m;
inline ui red(){
    ui data=0;char ch=0;
    ch=getchar();
    while(ch<'0' || ch>'9') ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return data;
}
struct node{
	int num,tim;
	friend inline bool operator<(const node&a,const node&b){return a.num>b.num||(a.num==b.num&&a.tim<b.tim);}
}val[N],w[N];
int ch[N][2],siz[N],pri[N],rt;
inline void pushup(const int&u){siz[u]=siz[ch[u][0]]+siz[ch[u][1]]+1;}
int merge(int x,int y){
	if(!x||!y)return x|y;
	if(pri[x]<pri[y])return ch[x][1]=merge(ch[x][1],y),pushup(x),x;
	else return ch[y][0]=merge(x,ch[y][0]),pushup(y),y;
}
void split(int u,const node&v,int&x,int&y){
	if(!u)return(void)(x=y=0);
	if(val[u]<v)x=u,split(ch[u][1],v,ch[x][1],y);
	else y=u,split(ch[u][0],v,x,ch[y][0]);
	pushup(u);
}
ui last=7,seed;
inline ui ran(){
	seed=(seed<<4)+seed+last;
	return seed%m+1;
}
int build(int l,int r){
	if(l>=r)return 0;
	int mid=l+r>>1;siz[mid]=1;
	val[mid].num=0;val[mid].tim=0;
	ch[mid][0]=0;ch[mid][1]=0;
	if(l==r-1)return mid;
	ch[mid][0]=build(l,mid);
	ch[mid][1]=build(mid+1,r);
	return pushup(mid),mid;
}
int pre=0;
inline void del(const node&v){
	split(rt,v,l,r);
	split(r,(node){v.num,v.tim+1},p,r);pre=p;
	p=merge(ch[p][0],ch[p][1]);
	rt=merge(l,merge(p,r));
}
inline int add(const node&v){
	split(rt,v,l,r);
	int ans=siz[l];
	ch[pre][0]=0;ch[pre][1]=0;
	val[pre]=v;siz[pre]=1;
	rt=merge(l,merge(pre,r));
	return ans;
}
inline void print(ui x){
    if(x>9)print(x/10);
    putchar(x%10+'0');
}
int main(){
	int T_T=red();
	for(int re i=414;i<=827;++i)srand(rand());//不要问我这两个数有没有什么特别的含义,我不会告诉你的
	for(int re i=1;i^100001;++i)pri[i]=rand();
	while(T_T--){
		m=red();n=red();seed=red();
		memset(w,0,sizeof(w));
		rt=build(1,m+1);
		while(n--){l=r=p=0;
			a=ran(),b=ran();del(w[a]);
			++w[a].num;w[a].tim+=b;
			print(last=add(w[a]));
			putchar('\n');
		}
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值