洛谷P5338 [TJOI2019]甲苯先生的滚榜
原题链接洛谷P5338 [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 * + last;
return seed % m + ;
}
(last为上一次输出的结果,在没有输出结果时last=7)
要求每次生成两个数据Ria, Rib 表示Ria的人Accept了一道题目,他的罚时为Rib。(也就是说Ria的题目数量+1,罚时长度+Rib)。
要求一共生成n组数据,代表一共有n次提交
对于所有数据,保证罚时总和不超过1500000
输出格式:
每次提交输出一行整数,表示Ria在ac后比Ria成绩高的有多少选手。
输入输出样例
说明
测试点# 1, 2 3, 4 5 6, 7, 8 9, 10
T ≤10 ≤5 ≤15 ≤5 ≤5
m ≤1000 ≤10000 ≤10^5 ≤10^4 ≤10^5
n ≤1000 ≤10000 ≤10^5 ≤10^6 ≤10^6
题解
Method1:平衡树
这题要动态维护排名,很容易想到平衡树(根据两个关键字维护排序二叉树)
实现:
0.初始化
(1)定义node表示状态,包含AC数目,罚时两个成员,建立平衡树
struct node{
int ac,fine;
node(){
ac=fine=;
}
node(int x,int y){
ac=x;
fine=y;
}
friend inline bool operator<(node x,node y){
if(x.ac!=y.ac)
return x.ac>y.ac;
return x.fine<y.fine;
}
}key[MAXN],A[MAXN];
1.更新
查找原状态并删除(A是按照编号查询的备份状态),更新状态并插入
if(A[ii].ac)
del(A[ii]);
A[ii].ac++;
A[ii].fine+=jj;
ins(A[ii]);
2.查询排名
直接查找find的排名即可
last=find(A[ii]);
printf("%d\n",last);
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+,MAXN=1e5+;
struct node{
int ac,fine;
node(){
ac=fine=;
}
node(int x,int y){
ac=x;
fine=y;
}
friend inline bool operator<(node x,node y){
if(x.ac!=y.ac)
return x.ac>y.ac;
return x.fine<y.fine;
}
friend inline bool operator==(node x,node y){
return x.ac==y.ac&&x.fine==y.fine;
}
}key[MAXN],A[MAXN];
int cnt[MAXN],ch[MAXN][],siz[MAXN],f[MAXN];
int root,sz;
inline void clear(int x){
key[x].ac=key[x].fine=cnt[x]=ch[x][]=ch[x][]=siz[x]=f[x]=;
}
inline int get(int x){
return x==ch[f[x]][];
}
inline void upd(int x){
if(x){
siz[x]=cnt[x];
if(ch[x][]){
siz[x]+=siz[ch[x][]];
}
if(ch[x][]){
siz[x]+=siz[ch[x][]];
}
}
}
inline void rotate(int x){
int fa=f[x],gf=f[fa],which=get(x);
ch[fa][which]=ch[x][which^];
f[ch[fa][which]]=fa;
ch[x][which^]=fa;
f[fa]=x;
f[x]=gf;
if(gf){
ch[gf][ch[gf][]==fa]=x;
}
upd(fa);
upd(x);
}
inline void splay(int x){
for(int fa;(fa=f[x]);rotate(x)){
if(f[fa]){
rotate(get(x)==get(fa)?fa:x);
}
}
root=x;
}
inline void ins(node x){
if(!root){
sz++;
clear(sz);
root=sz;
cnt[sz]=siz[sz]=;
key[sz]=x;
return;
}
int cur=root,fa=;
while(){
if(x==key[cur]){
cnt[cur]++;
upd(cur);
upd(fa);
splay(cur);
return;
}
fa=cur;
cur=ch[fa][key[fa]<x];
if(!cur){
clear(++sz);
f[sz]=fa;
cnt[sz]=siz[sz]=;
ch[fa][key[fa]<x]=sz;
key[sz]=x;
upd(fa);
splay(sz);
return;
}
}
}
inline int find(node x){
int cur=root,ret=;
while(){
if(x<key[cur]){
cur=ch[cur][];
}else{
ret+=(ch[cur][]?siz[ch[cur][]]:);
if(key[cur]==x){
splay(cur);
return ret;/*return ret+1*/
}
ret+=cnt[cur];
cur=ch[cur][];
}
}
}
inline node findx(int x){
int cur=root;
while(){
if(ch[cur][]&&x<=siz[ch[cur][]]){
cur=ch[cur][];
}else{
int tmp=(ch[cur][]?siz[ch[cur][]]:)+cnt[cur];
if(x<=tmp){
return key[cur];
}
x-=tmp;
cur=ch[cur][];
}
}
}
inline int pre(){
int cur=ch[root][];
while(ch[cur][]){
cur=ch[cur][];
}
return cur;
}
inline int nxt(){
int cur=ch[root][];
while(ch[cur][]){
cur=ch[cur][];
}
return cur;
}
inline void del(node x){
find(x);
if(cnt[root]>){
cnt[root]--;
upd(root);
return;
}
if(!ch[root][]&&!ch[root][]){
clear(root);
root=;
return;
}
if(!ch[root][]){
int old=root;
root=ch[root][];
f[root]=;
clear(old);
return;
}
if(!ch[root][]){
int old=root;
root=ch[root][];
f[root]=;
clear(old);
return;
}
int old=root,p=pre();
splay(p);
ch[root][]=ch[old][];
f[ch[old][]]=root;
clear(old);
upd(root);
}
int Task;
int N;
unsigned int M,seed,last=;
inline unsigned int randNum() {
seed=seed*+last;
return seed%M+;
}
int main(){
scanf("%d",&Task);
while(Task--){
scanf("%d%d%d",&M,&N,&seed);
root=sz=;
memset(A,,sizeof(A));
memset(cnt,,sizeof(cnt));
memset(ch,,sizeof(ch));
memset(siz,,sizeof(siz));
memset(f,,sizeof(f));
for(int i=;i<=N;i++){
int ii=randNum(),jj=randNum();
if(A[ii].ac)
del(A[ii]);
A[ii].ac++;
A[ii].fine+=jj;
ins(A[ii]);
last=find(A[ii]);
printf("%d\n",last);
}
}
return ;
}
splay不够快,但又不会别的算法,怎么办?用红黑树rb_tree!
rb_tree代码太长,不会写怎么办?用平板电视pbds!
#include<bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;
using namespace std;
typedef long long LL;
const int MAXN=1e5+;
const LL INF=1.5e14,UNIT=1e7;
struct node{
int ac,order;
LL fine;
node(){ ac=fine=order=; }
node(int x,int y,int z){ ac=x; fine=y; order=z; }
friend bool operator<(node x,node y){
if(x.ac!=y.ac)
return x.ac>y.ac;
if(x.fine!=y.fine)
return x.fine<y.fine;
return x.order<y.order;
}
};
tree<node,null_type,less<node>,rb_tree_tag,tree_order_statistics_node_update> q;
int Task;
int N;
unsigned M,seed,last=;
inline unsigned int randNum() {
seed=seed*+last;
return seed%M+;
}
int ac[MAXN];
LL fine[MAXN];
int main(){
scanf("%d",&Task);
while(Task--){
scanf("%d%d%d",&M,&N,&seed);
q.clear();
memset(ac,,sizeof(ac));
memset(fine,,sizeof(fine));
for(unsigned int i=;i<=M;i++){
q.insert(node(,fine[i],i));
}
for(int i=;i<=N;i++){
unsigned int ii=randNum();
unsigned long long jj=randNum();
q.erase(node(ac[ii],fine[ii],ii));
ac[ii]++;
fine[ii]+=jj;
q.insert(node(ac[ii],fine[ii],ii));
last=q.order_of_key(node(ac[ii],fine[ii],));
printf("%d\n",last);
}
}
return ;
}
Method2:权值线段树
本题有两个关键字,并且第一关键字AC数量非常小,我们可以用一个树状数组维护第一关键字的前缀和
对于第二关键字,我们可以对于每一个第一关键字建一棵权值线段树统计数量,递归查找前缀和
具体实现过程略,直接贴zhousuyu大佬的代码
#include <cstdio>
#include <algorithm>
using namespace std;
#define ui unsigned int
const int N = ;
int Case, n, m, a[N], b[N], c[N], ls[N * ], rs[N * ], sum[N * ], rt[N], tot, lst = ;
ui seed;
ui rng() {
seed = seed * + lst;
return seed % m + ;
}
void modify(int &x, int l, int r, int p, int v) {
if (!x)
x = ++tot, ls[x] = rs[x] = sum[x] = ;
sum[x] += v;
if (l == r)
return;
int mid = l + r >> ;
p <= mid ? modify(ls[x], l, mid, p, v) : modify(rs[x], mid + , r, p, v);
}
int query(int x, int l, int r, int p) {
if (l == r)
return sum[x];
int mid = l + r >> ;
return p <= mid ? query(ls[x], l, mid, p) : sum[ls[x]] + query(rs[x], mid + , r, p);
}
void mdf(int x, int v) {
while (x) c[x] += v, x ^= x & -x;
}
int qry(int x) {
int s = ;
while (x < N) s += c[x], x += x & -x;
return s;
}
int main() {
scanf("%d", &Case);
while (Case--) {
scanf("%d%d%u", &m, &n, &seed);
for (int i = ; i < N; ++i) a[i] = b[i] = c[i] = rt[i] = ;
tot = ;
for (int i = ; i <= n; ++i) {
int x = rng(), y = rng();
if (a[x])
modify(rt[a[x]], , N, b[x], -), mdf(a[x], -);
++a[x];
b[x] += y;
modify(rt[a[x]], , N, b[x], );
mdf(a[x], );
printf("%d\n", lst = query(rt[a[x]], , N, b[x] - ) + qry(a[x] + ));
}
}
return ;
}
洛谷P5338 [TJOI2019]甲苯先生的滚榜的更多相关文章
- 【题解】Luogu P5338 [TJOI2019]甲苯先生的滚榜
原题传送门 这题明显可以平衡树直接大力整,所以我要说一下线段树+树状数组的做法 实际线段树+树状数组的做法也很暴力 我们先用树状数组维护每个ac数量有多少个队伍.这样就能快速求出有多少队伍ac数比现在 ...
- luogu P5338 [TJOI2019]甲苯先生的滚榜
传送门 首先,排名系统,一看就知道是原题,可以上平衡树来维护 然后考虑一种比较朴素的想法,因为我们要知道排名在一个人前面的人数,也就是AC数比他多的人数+AC数一样并且罚时少的人数,所以考虑维护那两个 ...
- [TJOI2019]甲苯先生的滚榜——非旋转treap
题目链接: [TJOI2019]甲苯先生的滚榜 要求维护一个二维权值的集合并支持单点修改,用平衡树维护即可. 因为$n\le 10^6$但$m\le 10^5$,所以最多只有$10^5$个人被操作. ...
- 洛谷P5341 [TJOI2019]甲苯先生和大中锋的字符串
原题链接P5341 [TJOI2019]甲苯先生和大中锋的字符串 题目描述 大中锋有一个长度为 n 的字符串,他只知道其中的一个子串是祖上传下来的宝藏的密码.但是由于字符串很长,大中锋很难将这些子串一 ...
- BZOJ5509: [Tjoi2019]甲苯先生的滚榜
题解 开n个平衡树对每个AC数维护罚时,然后不同AC数用树状数组维护即可. 其实挺好写的...就是评测的时候评的巨久... #include <bits/stdc++.h> using n ...
- LG5338/BZOJ5509/LOJ3105 「TJOI2019」甲苯先生的滚榜 Treap
问题描述 LG5338 LOJ3105 BZOJ5509 题解 建立一棵\(\mathrm{Treap}\),把原来的\(val\)换成两个值\(ac,tim\) 原来的比较\(val_a<va ...
- [洛谷P5340][TJOI2019]大中锋的游乐场
题目大意:有$n(n\leqslant10^4)$个点,$m(m\leqslant10^5)$条边的无向图,每个点有一个属性$A/B$,要求$|cnt_A-cnt_B|\leqslant k(k\le ...
- 「TJOI2019」甲苯先生的滚榜
题目链接 问题分析 参照数据范围,我们需要一个能够在\(O(n\log n)\)复杂度内维护有序数列的数据结构.那么平衡树是很好的选择.参考程序中使用带旋Treap. 参考程序 #pragma GCC ...
- 洛谷 P1471 方差
洛谷 P1471 方差 题目背景 滚粗了的HansBug在收拾旧数学书,然而他发现了什么奇妙的东西. 题目描述 蒟蒻HansBug在一本数学书里面发现了一个神奇的数列,包含N个实数.他想算算这个数列的 ...
随机推荐
- 洛谷P2184——贪婪大陆
传送门:QAQQAQ 题意:给一个长度为$n$的区间,每次可以进行两种操作: 1.在$[l,r]$这个区间里放置一个和之前种类不同的炸弹 2.查询在$[l,r]$区间内有多少种不同种类的炸弹 思路:第 ...
- Firefox Developer Edition 是专为开发者设计
Firefox Developer Edition 当前是基于 Firefox 35.0a2,这款全新的浏览器包括内建调试功能,集成类似于Firefox火狐工具适配器的专用工具,并在浏览器当中内建We ...
- UVA 12304 /// 圆的综合题 圆的模板
题目大意: ①给出三角形三个点,求三角形外接圆,求外接圆的圆心和半径. ②给出三角形三个点,求三角形内接圆,求内接圆的圆心和半径. ③给出一个圆,和一个点,求过该点的圆的切线与x轴的夹角(0<= ...
- Codeforces 479【F】div3
题目链接:http://codeforces.com/problemset/problem/977/F 题意:给你一串数字序列,让你求最长上升子序列,但是这个子序列呢,它的数字得逐渐连续挨着. 题解: ...
- API文档管理工具
系统庞大之后,前后端分离开发,前端调用后端提供的接口,请求协议一般是 HTTP,数据格式一般是 JSON.后台只负责数据的提供和计算,而完全不处理展现逻辑和样式:前端则负责拿到数据,组织数据并展现的工 ...
- Error resolving template,template might not exist or might not be accessible by any of the configured Template Resolvers
template might not exist or might not be accessible by any of the configured Template Resolvers at o ...
- selenium基础-跳过验证码
selenium基础-跳过验证码 一.方法 设置万能验证码或者屏蔽验证码(最常用的方法) 使用验证码识别工具识别验证码 通过selenium操作cookies 直接使用配置文件的webdriver 二 ...
- Guarded Suspention 要等到我准备好
线程在运行过程中需要停下来等待,然后再继续执行. 范例程序,一个线程(ClientThread)对另外一个线程(ServerThread)传递请求,实现一个模拟消息通道的功能. public clas ...
- 2018-9-29-Roslyn-通过-Nuget-引用源代码-在-VS-智能提示正常但是无法编译
title author date CreateTime categories Roslyn 通过 Nuget 引用源代码 在 VS 智能提示正常但是无法编译 lindexi 2018-09-29 1 ...
- Python编程从入门到实践
Python编程从入门到实践1 起步2 变量和简单数据类型3 列表简介4 操作列表5 if语句6 字典7 用户输入和while循环8 函数9 类10 文件和异常11 测试代码12 武装飞船13 外星人 ...