洛谷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个实数.他想算算这个数列的 ...
随机推荐
- L1正则化可以解决过拟合问题(稀疏解)
损失函数最小,也就是求极值点,也就是损失函数导数为0.上面也说了,如果d0+λ和d0-λ为异号的时候刚好为极值点,损失函数导数为0(w为0是条件).而对于L2正则化,在w=0时并不一定是极值点而是d0 ...
- 顺时针打印矩阵元素(python实现)
我觉得我的算法比较简单易懂,比网上的那些简单些.至于时间复杂度就没有比较了. 算法思想:从最外层向内层遍历矩阵 # my algorithmimport math def print_matrix(m ...
- ps-通道错位制作奇幻海报
效果图 素材 1.载入素材 点击通道 点击下面的蓝色通道,全选-复制 点击绿色通道,全选-黏贴 编辑-变化-水平翻转 点击RGB即可.
- EXE 和 SYS 信息交互
操了,分发函数少发一个,让我白调了两个多小时.
- 2019-6-23-WPF-解决弹出模态窗口关闭后,主窗口不在最前
title author date CreateTime categories WPF 解决弹出模态窗口关闭后,主窗口不在最前 lindexi 2019-06-23 11:48:38 +0800 20 ...
- anaconda新建环境
安装tensorflow等如下: https://blog.csdn.net/Gransand/article/details/80713810 修改默认打开目录如下: https://blog.cs ...
- 时间 '2018-08-06T10:00:00.000Z' 格式转化为本地时间(转)
原文:https://blog.csdn.net/sxf_123456/article/details/81582964 from datetime import datetime,timedelta ...
- openwrt xfrp移植
对开源软件表示支持 https://github.com/KunTengRom/xfrp 上传编译,选择 cp .config xxx make 刷机 客户端配置文件: /tmp/etc# cat x ...
- leetcood学习笔记-3-无重复字符的最长子串
题目描述: 方法一:O(N) class Solution: def lengthOfLongestSubstring(self, s: str) -> int: slow = 0 fast = ...
- case in
#!/bin/bash source /etc/profilesource ~/.bashrc #自己定义$version_number case $version_number in3.0.17) ...