luogu P5338 [TJOI2019]甲苯先生的滚榜

Posted smyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P5338 [TJOI2019]甲苯先生的滚榜相关的知识,希望对你有一定的参考价值。

传送门

首先,排名系统,一看就知道是原题,可以上平衡树来维护

然后考虑一种比较朴素的想法,因为我们要知道排名在一个人前面的人数,也就是AC数比他多的人数+AC数一样并且罚时少的人数,所以考虑维护那两个东西.AC数更多的人数显然可以直接上树状数组.后者的话可以对每一种AC数开值域线段树,存每个罚时有多少人,注意到罚时之和不会超过\(1.5*10^6\),所以动态开点线段树可以轻松解决.然后每次有个人AC数和罚时改变就先在原来的位置-1,然后在新位置+1.每次询问就是树状数组上AC数\(>\)当前AC数的对应区间之和\(+\)对应线段树上罚时\(<\)当前罚时的区间之和

// luogu-judger-enable-o2
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#define LL long long
#define ui unsigned int

using namespace std;
const int N=1e5+10,M=1e6+10,MX=1500000;
LL rd()
{
    LL x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
ui randNum(ui& seed ,ui last,const ui m)
{ 
    seed=seed*17+last;
    return seed%m+1; 
}
ui sd;
int m,n,a[N],b[N],c[M];
void ad(int x,int y){while(x<=n) c[x]+=y,x+=x&(-x);}
int gsm(int x){int an=0;while(x) an+=c[x],x-=x&(-x);return an;}
int s[M*25],ch[M*25][2],rt[M],tt;
void add(int o1,int o2,int x)
{
    int l=1,r=MX;
    s[o1]=s[o2]+1;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(x<=mid)
        {
            ch[o1][0]=++tt,ch[o1][1]=ch[o2][1];
            o1=ch[o1][0],o2=ch[o2][0];
            r=mid;
        }
        else
        {
            ch[o1][0]=ch[o2][0],ch[o1][1]=++tt;
            o1=ch[o1][1],o2=ch[o2][1];
            l=mid+1;
        }
        s[o1]=s[o2]+1;
    }
}
void dec(int o,int x)
{
    int l=1,r=MX;
    --s[o];
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(x<=mid) o=ch[o][0],r=mid;
        else o=ch[o][1],l=mid+1;
        --s[o];
    }
}
int quer(int o,int l,int r,int ll,int rr)
{
    if(ll>rr||!o) return 0;
    if(ll<=l&&r<=rr) return s[o];
    int an=0,mid=(l+r)>>1;
    if(ll<=mid) an+=quer(ch[o][0],l,mid,ll,rr);
    if(rr>mid) an+=quer(ch[o][1],mid+1,r,ll,rr);
    return an;
}
    

int main()
{
    int T=rd(),ans=7;
    while(T--)
    {
        m=rd(),n=rd(),sd=rd();
        for(int i=1;i<=n;++i) c[i]=rt[i]=0;
        for(int i=1;i<=m;++i) a[i]=b[i]=0;
        tt=0;
        for(int i=1;i<=n;++i)
        {
            int x=randNum(sd,ans,m),y=randNum(sd,ans,m);
            if(a[x]) dec(rt[a[x]],b[x]),ad(a[x],-1);
            ++a[x],b[x]+=y;
            int las=rt[a[x]];
            add(rt[a[x]]=++tt,las,b[x]),ad(a[x],1);
            ans=gsm(n)-gsm(a[x])+quer(rt[a[x]],1,MX,1,b[x]-1);
            printf("%d\n",ans);
        }
    }
    return 0;
}

以上是关于luogu P5338 [TJOI2019]甲苯先生的滚榜的主要内容,如果未能解决你的问题,请参考以下文章

题解Luogu P5337 [TJOI2019]甲苯先生的字符串

题解Luogu P5342 [TJOI2019]甲苯先生的线段树

TJOI2019甲苯先生和大中锋的字符串

「TJOI2019」甲苯先生的滚榜

[TJOI2019]甲苯先生和大中锋的字符串

「TJOI2019」甲苯先生的滚榜