洛谷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个实数.他想算算这个数列的 ...
随机推荐
- 2019 USP Try-outs 练习赛
// 好久没更博客了,最近打了很多场练习赛&校内PK赛,大概自闭忙于补题吧 // 9.26 周四练习赛 A. Kolkhozy 题意 有 n 个数 \(f[i]\) ,有 q 次询问(l, r ...
- java 在数组{1,2,3,4,6,7,8,9,10}中插入一个数5,使其插入完成后仍然有序
1.需要实现的效果 2.代码实现 import java.util.Scanner; /* * 11.在数组{1,2,3,4,6,7,9,8,10}中插入一个数5, * 使其插入完成后仍然有序,运行结 ...
- windows使用cmd查看、杀死进程
查看某个进程: netstat -ano | findstr 端口号 杀死某个进程: taskkill /f /pid 进程号
- 同步图计算实现pageRank算法
pageRank算法是Google对网页重要性的打分算法. 一个用户浏览一个网页时,有85%的可能性点击网页中的超链接,有15%的可能性转向任意的网页.pageRank算法就是模拟这种行为. Rv:定 ...
- 整理下webapi的一些琐碎事情
在使用webapi的时候我们会遇到一些问题比如 1.POST怎么请求 2.怎么兼容JSONP请求 3.怎么给指定端提供跨域的请求 4.怎么显示单独的models层的注释 问题一二其他人都玩的比较成熟的 ...
- python open函数关于w+ r+ 读写操作的理解(转)
r 只能读 (带r的文件必须先存在)r+ 可读可写 不会创建不存在的文件.如果直接写文件,则从顶部开始写,覆盖之前此位置的内容,如果先读后写,则会在文件最后追加内容.w+ 可读可写 如果文件存在 则覆 ...
- Mysql 权限命令整理大全
mysql show slave status 需要什么权限 grant replication client on *.* to 'user_name'@'%';
- HTML --- 简单的标签
HTML --- 简单的标签 html概述和基本结构 html概述 HTML是 HyperText Mark-up Language 的首字母简写,意思是超文本标记语言,超文本指的是超链接,标记指的是 ...
- thinkphp 系统流程
用户URL请求 调用应用入口文件(通常是网站的index.php) 载入框架入口文件(ThinkPHP.php) 记录初始运行时间和内存开销 系统常量判断及定义 载入框架引导类(Think\Think ...
- python相关软件安装流程图解——MySQL 8.0.13安装教程(windows 64位)——MYSQL依赖的软件——MYSQL必须的系统DLL插件——MYSQL真正的安装
https://www.mysql.com/https://www.mysql.com/downloads/https://dev.mysql.com/downloads/windows/https: ...