洛谷P5338 [TJOI2019]甲苯先生的滚榜

原题链接洛谷P5338 [TJOI2019]甲苯先生的滚榜

题目描述

甲苯先生在制作一个online judge,他发现做比赛的人们很关心自己的排名(显而易见),在acm赛制的比赛中,如果通过题目数量不相等,则通过题目数量多的人排名更靠前,如果通过题目数量相等,

则罚时更少的人排名更高。甲苯先生想让大家帮忙设计一个程序,每次有人通过之后,就告诉他排名在他的前面有多少人。(不包括和他罚时题数都相同的同学)

输入输出格式

输入格式:

 

第一行输入一个整数T表示样例数。

对于每一个样例:输入三个整数m, n, seed。m表示参赛总人数(编号1m),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:  复制
1
7 3 1
输出样例#1:  复制
0
1
0

说明 

测试点#  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=0;
    }
    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);

splay代码(因为我写的splay常数极大,TLE了)

  1 #include
  2 using namespace std;
  3 typedef long long LL;
  4 const int INF=1e9+7,MAXN=1e5+5;
  5 struct node{
  6     int ac,fine;
  7     node(){
  8         ac=fine=0;
  9     }
 10     node(int x,int y){
 11         ac=x;
 12         fine=y;
 13     }
 14     friend inline bool operator<(node x,node y){
 15         if(x.ac!=y.ac)
 16             return x.ac>y.ac;
 17         return x.fine<y.fine;
 18     }
 19     friend inline bool operator==(node x,node y){
 20         return x.ac==y.ac&&x.fine==y.fine;
 21     }
 22 }key[MAXN],A[MAXN];
 23 int cnt[MAXN],ch[MAXN][2],siz[MAXN],f[MAXN];
 24 int root,sz;
 25 inline void clear(int x){
 26     key[x].ac=key[x].fine=cnt[x]=ch[x][0]=ch[x][1]=siz[x]=f[x]=0;
 27 }
 28 inline int get(int x){
 29     return x==ch[f[x]][1];
 30 }
 31 inline void upd(int x){
 32     if(x){
 33         siz[x]=cnt[x];
 34         if(ch[x][0]){
 35             siz[x]+=siz[ch[x][0]];
 36         }
 37         if(ch[x][1]){
 38             siz[x]+=siz[ch[x][1]];
 39         }
 40     }
 41 }
 42 inline void rotate(int x){
 43     int fa=f[x],gf=f[fa],which=get(x);
 44     ch[fa][which]=ch[x][which^1];
 45     f[ch[fa][which]]=fa; 
 46     ch[x][which^1]=fa;
 47     f[fa]=x;
 48     f[x]=gf;
 49     if(gf){
 50         ch[gf][ch[gf][1]==fa]=x;
 51     }
 52     upd(fa);
 53     upd(x);
 54 }
 55 inline void splay(int x){
 56     for(int fa;(fa=f[x]);rotate(x)){
 57         if(f[fa]){
 58             rotate(get(x)==get(fa)?fa:x);
 59         }
 60     }
 61     root=x;
 62 }
 63 inline void ins(node x){
 64     if(!root){
 65         sz++;
 66         clear(sz);
 67         root=sz;
 68         cnt[sz]=siz[sz]=1;
 69         key[sz]=x;
 70         return;
 71     }
 72     int cur=root,fa=0;
 73     while(1){
 74         if(x==key[cur]){
 75             cnt[cur]++;
 76             upd(cur);
 77             upd(fa);
 78             splay(cur);
 79             return;
 80         }
 81         fa=cur;
 82         cur=ch[fa][key[fa]<x];
 83         if(!cur){
 84             clear(++sz);
 85             f[sz]=fa;
 86             cnt[sz]=siz[sz]=1;
 87             ch[fa][key[fa]sz;
 88             key[sz]=x;
 89             upd(fa);
 90             splay(sz);
 91             return;
 92         }
 93     }
 94 }
 95 inline int find(node x){
 96     int cur=root,ret=0;
 97     while(1){
 98         if(x<key[cur]){
 99             cur=ch[cur][0];
100         }else{
101             ret+=(ch[cur][0]?siz[ch[cur][0]]:0);
102             if(key[cur]==x){
103                 splay(cur);
104                 return ret;/*return ret+1*/
105             }
106             ret+=cnt[cur];
107             cur=ch[cur][1];
108         }
109     }
110 }
111 inline node findx(int x){
112     int cur=root;
113     while(1){
114         if(ch[cur][0]&&x<=siz[ch[cur][0]]){
115             cur=ch[cur][0];
116         }else{
117             int tmp=(ch[cur][0]?siz[ch[cur][0]]:0)+cnt[cur];
118             if(x<=tmp){
119                 return key[cur];
120             }
121             x-=tmp;
122             cur=ch[cur][1];
123         }
124     }
125 }
126 inline int pre(){
127     int cur=ch[root][0];
128     while(ch[cur][1]){
129         cur=ch[cur][1];
130     }
131     return cur;
132 }
133 inline int nxt(){
134     int cur=ch[root][1];
135     while(ch[cur][0]){
136         cur=ch[cur][0];
137     }
138     return cur;
139 }
140 inline void del(node x){
141     find(x);
142     if(cnt[root]>1){
143         cnt[root]--;
144         upd(root);
145         return;
146     }
147     if(!ch[root][0]&&!ch[root][1]){
148         clear(root);
149         root=0;
150         return;
151     }
152     if(!ch[root][0]){
153         int old=root;
154         root=ch[root][1];
155         f[root]=0;
156         clear(old);
157         return;
158     }
159     if(!ch[root][1]){
160         int old=root;
161         root=ch[root][0];
162         f[root]=0;
163         clear(old);
164         return;
165     }
166     int old=root,p=pre();
167     splay(p);
168     ch[root][1]=ch[old][1];
169     f[ch[old][1]]=root;
170     clear(old);
171     upd(root);
172 }
173 int Task;
174 int N;
175 unsigned int M,seed,last=7;
176 inline unsigned int randNum() {
177     seed=seed*17+last;
178     return seed%M+1;
179 }
180 int main(){
181     scanf("%d",&Task);
182     while(Task--){
183         scanf("%d%d%d",&M,&N,&seed);
184         root=sz=0;
185         memset(A,0,sizeof(A));
186         memset(cnt,0,sizeof(cnt));
187         memset(ch,0,sizeof(ch));
188         memset(siz,0,sizeof(siz));
189         memset(f,0,sizeof(f));
190         for(int i=1;i<=N;i++){
191             int ii=randNum(),jj=randNum();
192             if(A[ii].ac)
193                 del(A[ii]);
194             A[ii].ac++;
195             A[ii].fine+=jj;
196             ins(A[ii]);
197             last=find(A[ii]);
198             printf("%d\n",last);
199         }
200     }
201     return 0;
202 }
View Code

 

splay不够快,但又不会别的算法,怎么办?用红黑树rb_tree

rb_tree代码太长,不会写怎么办?用平板电视pbds

rb_tree代码

 1 #include
 2 #include 
 3 #include 
 4 using namespace __gnu_pbds;
 5 using namespace std;
 6 typedef long long LL;
 7 const int MAXN=1e5+7;
 8 const LL INF=1.5e14,UNIT=1e7;
 9 struct node{
10     int ac,order;
11     LL fine;
12     node(){ ac=fine=order=0; }
13     node(int x,int y,int z){ ac=x; fine=y; order=z; }
14     friend bool operator<(node x,node y){
15         if(x.ac!=y.ac)
16             return x.ac>y.ac;
17         if(x.fine!=y.fine)
18             return x.fine<y.fine;
19         return x.order<y.order;
20     }
21 };
22 tree,rb_tree_tag,tree_order_statistics_node_update> q;
23 int Task;
24 int N;
25 unsigned M,seed,last=7;
26 inline unsigned int randNum() {
27     seed=seed*17+last;
28     return seed%M+1;
29 }
30 int ac[MAXN];
31 LL fine[MAXN];
32 int main(){
33     scanf("%d",&Task);
34     while(Task--){
35         scanf("%d%d%d",&M,&N,&seed);
36         q.clear();
37         memset(ac,0,sizeof(ac));
38         memset(fine,0,sizeof(fine));
39         for(unsigned int i=1;i<=M;i++){
40             q.insert(node(0,fine[i],i));
41         }
42         for(int i=1;i<=N;i++){
43             unsigned int ii=randNum();
44             unsigned long long jj=randNum();
45             q.erase(node(ac[ii],fine[ii],ii));
46             ac[ii]++;
47             fine[ii]+=jj;
48             q.insert(node(ac[ii],fine[ii],ii));
49             last=q.order_of_key(node(ac[ii],fine[ii],0));
50             printf("%d\n",last);
51         }
52     }
53     return 0;
54 }
View Code

 Method2:权值线段树

 本题有两个关键字,并且第一关键字AC数量非常小,我们可以用一个树状数组维护第一关键字的前缀和

对于第二关键字,我们可以对于每一个第一关键字建一棵权值线段树统计数量,递归查找前缀和

具体实现过程略,直接贴zhousuyu大佬的代码

 1 #include 
 2 #include 
 3 using namespace std;
 4 #define ui unsigned int
 5 const int N = 1500005;
 6 int Case, n, m, a[N], b[N], c[N], ls[N * 20], rs[N * 20], sum[N * 20], rt[N], tot, lst = 7;
 7 ui seed;
 8 ui rng() {
 9     seed = seed * 17 + lst;
10     return seed % m + 1;
11 }
12 void modify(int &x, int l, int r, int p, int v) {
13     if (!x)
14         x = ++tot, ls[x] = rs[x] = sum[x] = 0;
15     sum[x] += v;
16     if (l == r)
17         return;
18     int mid = l + r >> 1;
19     p <= mid ? modify(ls[x], l, mid, p, v) : modify(rs[x], mid + 1, r, p, v);
20 }
21 int query(int x, int l, int r, int p) {
22     if (l == r)
23         return sum[x];
24     int mid = l + r >> 1;
25     return p <= mid ? query(ls[x], l, mid, p) : sum[ls[x]] + query(rs[x], mid + 1, r, p);
26 }
27 void mdf(int x, int v) {
28     while (x) c[x] += v, x ^= x & -x;
29 }
30 int qry(int x) {
31     int s = 0;
32     while (x < N) s += c[x], x += x & -x;
33     return s;
34 }
35 int main() {
36     scanf("%d", &Case);
37     while (Case--) {
38         scanf("%d%d%u", &m, &n, &seed);
39         for (int i = 0; i < N; ++i) a[i] = b[i] = c[i] = rt[i] = 0;
40         tot = 0;
41         for (int i = 1; i <= n; ++i) {
42             int x = rng(), y = rng();
43             if (a[x])
44                 modify(rt[a[x]], 0, N, b[x], -1), mdf(a[x], -1);
45             ++a[x];
46             b[x] += y;
47             modify(rt[a[x]], 0, N, b[x], 1);
48             mdf(a[x], 1);
49             printf("%d\n", lst = query(rt[a[x]], 0, N, b[x] - 1) + qry(a[x] + 1));
50         }
51     }
52     return 0;
53 }
View Code

 

 

 

转载于:https://www.cnblogs.com/guoshaoyang/p/10846405.html

你可能感兴趣的:(ui,数据结构与算法)