[TJOI2019]甲苯先生的滚榜——[splay]

【题目描述】

甲苯先生在制作一个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成绩高的有多少选手。

S a m p l e    I n p u t Sample~~Input Sample  Input

1
7 3 1

S a m p l e    O u t p u t Sample~~Output Sample  Output

0
1
0

【题意分析】

每次操作根据randNum ()函数生成Ria和Rib
ac[i]表示i位选手的ac数,fs[i]相应地表示罚时
那么每次操作 a c [ R i a ] + + , f s [ R i a ] + = R i b ac[Ria]++,fs[Ria]+=Rib ac[Ria]++,fs[Ria]+=Rib
有两个关键字,怎么办?上pair
然后套splay模板(记得一定要插哨兵,我就在这挂了),每个节点的值就是一个pair
pair的first是ac数,那么正常比较,second是罚时,越小的才排在前面,因此插入的时候取负就行了。

Code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#define MAXN 1200100
#define INF 1 << 29
using namespace std;
typedef unsigned int ui;
typedef pair <int, int> pii;

int size[MAXN], son[MAXN][3], father[MAXN], cnt[MAXN], ac[MAXN], fs[MAXN], sz, root, n;
pii val[MAXN];
ui m, seed, last = 7;

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

struct spaly {
    
    inline void maintain (int x) {
        size[x] = size[son[x][0]] + size[son[x][1]] + cnt[x];
    }
    
    inline void rotate (int x) {
        int y = father[x], z = father[y];
        int k = son[y][1] == x, kk = son[z][1] == y;
        son[z][kk] = x,
        father[x] = z,
        son[y][k] = son[x][k ^ 1],
        father[son[x][k ^ 1]] = y,
        son[x][k ^ 1] = y,
        father[y] = x,
        maintain (y), maintain (x);
    }
    
    inline void splay (int x, int goal) {
        while (father[x] != goal) {
            int y = father[x], z = father[y];
            if (z != goal)
                (son[y][1] == x) ^ (son[z][1] == y)
                    ? rotate (x) : rotate (y);
            rotate (x);
        }
        if (! goal) root = x;
    }
    
    inline void find (pii x) {
        int now = root;
        while (son[now][x > val[now]] && val[now] != x)
            now = son[now][x > val[now]];
        splay (now, 0);
    }
    
    inline int prev (pii x) {
        int now = root, pre;
        while (now) {
            (val[now] < x)
                ? pre = now, now = son[now][1]
                : now = son[now][0];
        }
        return pre;
    }
    
    inline int succ (pii x) {
        int now = root, nxt;
        while (now) {
            (val[now] > x)
                ? nxt = now, now = son[now][0]
                : now = son[now][1];
        }
        return nxt;
    }
    
    inline void del (pii x) {
        int pre = prev (x), nxt = succ (x);
        splay (pre, 0), splay (nxt, pre);
        int pos = son[nxt][0];
        if (cnt[pos] > 1) cnt[pos]--, splay (pos, 0);
        else son[nxt][0] = 0;
    }
    
    inline void insert (pii x) {
        int now = root, fa = 0;
        while (now && x != val[now]) fa = now, now = son[now][x > val[now]];
        if (now) cnt[now]++;
        else {
            now = ++sz;
            if (fa) son[fa][x > val[fa]] = now;
            son[now][0] = son[now][1] = 0;
            size[now] = cnt[now] = 1;
            val[now] = x, father[now] = fa;
        }
        splay (now, 0);
    }
    
}tree;

int main () {
    int T; scanf ("%d", &T);
    for (register int i = 1; i <= T; i++) {
    	scanf ("%d%d%d", &m, &n, &seed), root = 0, sz = 0;
    	tree.insert (pii (INF, -INF)), tree.insert (pii (-INF, INF));  
    	memset (ac, 0, sizeof ac), memset (fs, 0, sizeof fs);
    	for (register int j = 1; j <= n; j++) {
    		ui o = randNum (seed, last, m), u = randNum (seed, last, m);
            if (ac[o]) tree.del (pii (ac[o], -fs[o]));
            ++ac[o], fs[o] += u; tree.insert (pii (ac[o], -fs[o]));
            last = size[son[root][1]] - 1, printf ("%d\n", last);
        }
    }
    return 0;
}

然后你会发现你T飞了两个点,说明pair比较慢其实是我的程序常数太大
怎么办?加个火车头,pair换结构体+重载,勉强卡过10000ms。。。
为什么我的程序常数那么大

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
%:pragma GCC optimize("-fgcse")
%:pragma GCC optimize("-fgcse-lm")
%:pragma GCC optimize("-fipa-sra")
%:pragma GCC optimize("-ftree-pre")
%:pragma GCC optimize("-ftree-vrp")
%:pragma GCC optimize("-fpeephole2")
%:pragma GCC optimize("-ffast-math")
%:pragma GCC optimize("-fsched-spec")
%:pragma GCC optimize("unroll-loops")
%:pragma GCC optimize("-falign-jumps")
%:pragma GCC optimize("-falign-loops")
%:pragma GCC optimize("-falign-labels")
%:pragma GCC optimize("-fdevirtualize")
%:pragma GCC optimize("-fcaller-saves")
%:pragma GCC optimize("-fcrossjumping")
%:pragma GCC optimize("-fthread-jumps")
%:pragma GCC optimize("-funroll-loops")
%:pragma GCC optimize("-fwhole-program")
%:pragma GCC optimize("-freorder-blocks")
%:pragma GCC optimize("-fschedule-insns")
%:pragma GCC optimize("inline-functions")
%:pragma GCC optimize("-ftree-tail-merge")
%:pragma GCC optimize("-fschedule-insns2")
%:pragma GCC optimize("-fstrict-aliasing")
%:pragma GCC optimize("-fstrict-overflow")
%:pragma GCC optimize("-falign-functions")
%:pragma GCC optimize("-fcse-skip-blocks")
%:pragma GCC optimize("-fcse-follow-jumps")
%:pragma GCC optimize("-fsched-interblock")
%:pragma GCC optimize("-fpartial-inlining")
%:pragma GCC optimize("no-stack-protector")
%:pragma GCC optimize("-freorder-functions")
%:pragma GCC optimize("-findirect-inlining")
%:pragma GCC optimize("-fhoist-adjacent-loads")
%:pragma GCC optimize("-frerun-cse-after-loop")
%:pragma GCC optimize("inline-small-functions")
%:pragma GCC optimize("-finline-small-functions")
%:pragma GCC optimize("-ftree-switch-conversion")
%:pragma GCC optimize("-foptimize-sibling-calls")
%:pragma GCC optimize("-fexpensive-optimizations")
%:pragma GCC optimize("-funsafe-loop-optimizations")
%:pragma GCC optimize("inline-functions-called-once")
%:pragma GCC optimize("-fdelete-null-pointer-checks")
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#define MAXN 1200100
#define INF 1 << 29
using namespace std;
typedef unsigned int ui;

int size[MAXN], son[MAXN][3], father[MAXN], cnt[MAXN], ac[MAXN], fs[MAXN], sz, root, n;
ui m, seed, last = 7;

struct pii {
    int ac, fs;
    bool operator == (const pii &o) const {
        return ac == o.ac && fs == o.fs;
    }
    bool operator < (const pii &o) const {
        return ac < o.ac || (ac == o.ac && fs > o.fs);
    }
    bool operator > (const pii &o) const {
        return ac > o.ac || (ac == o.ac && fs < o.fs);
    }
    bool operator != (const pii &o) const {
        return ac != o.ac || (ac == o.ac && fs != o.fs);
    }
}val[MAXN];

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

void write (ui x) {
	if (x > 9) write (x / 10);
	putchar (x % 10 + '0');
}

struct spaly {
    
    inline void maintain (int x) {
        size[x] = size[son[x][0]] + size[son[x][1]] + cnt[x];
    }
    
    inline void rotate (int x) {
        int y = father[x], z = father[y];
        int k = son[y][1] == x, kk = son[z][1] == y;
        son[z][kk] = x,
        father[x] = z,
        son[y][k] = son[x][k ^ 1],
        father[son[x][k ^ 1]] = y,
        son[x][k ^ 1] = y,
        father[y] = x,
        maintain (y), maintain (x);
    }
    
    inline void splay (int x, int goal) {
        while (father[x] != goal) {
            int y = father[x], z = father[y];
            if (z != goal)
                (son[y][1] == x) ^ (son[z][1] == y)
                    ? rotate (x) : rotate (y);
            rotate (x);
        }
        if (! goal) root = x;
    }
    
    inline void find (pii x) {
        int now = root;
        while (son[now][x > val[now]] && val[now] != x)
            now = son[now][x > val[now]];
        splay (now, 0);
    }
    
    inline int prev (pii x) {
        int now = root, pre;
        while (now) {
            (val[now] < x)
                ? pre = now, now = son[now][1]
                : now = son[now][0];
        }
        return pre;
    }
    
    inline int succ (pii x) {
        int now = root, nxt;
        while (now) {
            (val[now] > x)
                ? nxt = now, now = son[now][0]
                : now = son[now][1];
        }
        return nxt;
    }
    
    inline void del (pii x) {
        int pre = prev (x), nxt = succ (x);
        splay (pre, 0), splay (nxt, pre);
        int pos = son[nxt][0];
        if (cnt[pos] > 1) cnt[pos]--, splay (pos, 0);
        else son[nxt][0] = 0;
    }
    
    inline void insert (pii x) {
        int now = root, fa = 0;
        while (now && x != val[now]) fa = now, now = son[now][x > val[now]];
        if (now) cnt[now]++;
        else {
            now = ++sz;
            if (fa) son[fa][x > val[fa]] = now;
            son[now][0] = son[now][1] = 0;
            size[now] = cnt[now] = 1;
            val[now] = x, father[now] = fa;
        }
        splay (now, 0);
    }
    
}tree;

int main () {
    int T; scanf ("%d", &T);
    for (register int i = 1; i <= T; i++) {
    	scanf ("%d%d%d", &m, &n, &seed), root = 0, sz = 0;
    	tree.insert (pii {INF, -INF}), tree.insert (pii {-INF, INF});  
    	memset (ac, 0, sizeof ac), memset (fs, 0, sizeof fs);
    	for (register int j = 1; j <= n; j++) {
    		ui o = randNum (seed, last, m), u = randNum (seed, last, m);
            if (ac[o]) tree.del (pii {ac[o], fs[o]});
            ++ac[o], fs[o] += u; tree.insert (pii {ac[o], fs[o]});
            last = size[son[root][1]] - 1, write (last), puts ("");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值