[Trie] BZOJ4896: [Thu Summer Camp2016]补退选

题意

X是T大的一名老师,每年他都要教授许多学生基础的C++知识。在T大,每个学生在每学期的开学前都需要选课,每次选课一共分为三个阶段:预选,正选,补退选;其中”补退选”阶段最忙碌。在补退选阶段,学生即可以选课,也可以退课。
对于X老师来说,在补退选阶段可能发生以下两种事件:
1:一个姓名为S的学生选了他的课(姓名S将出现在X的已选课学生名单中)
2:一个姓名为S的学生退了他的课(姓名S将从X的已选课学生名单中移除)
同时,X老师对于有哪些学生选了他的课非常关心,所以他会不定时的查询已选课学生名单,每次查询的格式如下:
最早在哪个事件之后,姓名以S为前缀的学生数量超过了vX老师看你骨骼惊奇,所以想用这个问题考考你,你当然不会畏惧,所以勇敢的接下了这个任务。
注意1:学生的姓名可能相同,如果有p个姓名相同的学生都选了X老师的课,则他们的姓名将出现在X老师的名单上p次。
注意2:只有已经选了课的学生才会退课,如果姓名为S的学生退课,则在他退课之前X老师的名单上一定有姓名S。
注意3:选课,退课和查询都被定义为”事件”,”事件”的编号从1开始。
n<=100000,字符串长度 <= 60,输入中的所有字符串只会包含前 10 个小写字母。
强制在线。

题解

看到前缀容易想到Trie,正常的插入删除,并在每个点上开一个vector记录该前缀出现次数递增变化的时刻序列,询问时在上面二分即可。

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
    char ch=gc(); int res=0,ff=1;
    while(!('0'<=ch&&ch<='9')){ if(ch=='-') ff=-1; ch=gc(); }
    while('0'<=ch&&ch<='9') res=(res<<3)+(res<<1)+ch-'0', ch=gc();
    return res*ff;
}
struct node{
    int cnt; vector< pair<int,int> > evt;
    node* ch[10];
    node(node* son=NULL){ cnt=0; evt.clear(); for(int i=0;i<=9;i++) ch[i]=son; }    
} nil, *null=&nil, *root=null;
typedef node* P_node;
int Q; long long lstans;
void Insert(P_node &p,char* now,int k){
    if(p==null) p=new node(null);
    p->cnt++; 
    int len=p->evt.size();
    if(len==0||p->evt[len-1].first<p->cnt) p->evt.push_back(make_pair(p->cnt,k));
    if((*now)=='\000') return; Insert(p->ch[(*now)-'a'],now+1,k);
}
void Erase(P_node p,char* now){
    p->cnt--; 
    if((*now)=='\000') return;
    Erase(p->ch[(*now)-'a'],now+1);
}
int Query(P_node p,char* now,int val){
    if(p==null) return -1;
    if((*now)=='\000'){
        int len=p->evt.size();
        if(len==0||p->evt[len-1].first<val) return -1;
        int L=0,R=len-1;
        while(L<=R){
            int mid=(L+R)>>1;
            if(p->evt[mid].first==val) return p->evt[mid].second;
            if(p->evt[mid].first<val) L=mid+1;
                                 else R=mid-1;
        }
    }
    return Query(p->ch[(*now)-'a'],now+1,val);
}
char st[65];
int main(){
    freopen("bzoj4896.in","r",stdin);
    freopen("bzoj4896.out","w",stdout);
    Q=getint();
    for(int ii=1;ii<=Q;ii++){
        int pd=getint(),m=-1;
        char ch=gc(); while(!('a'<=ch&&ch<='z')) ch=gc();
        for(;'a'<=ch&&ch<='z';ch=gc()) st[++m]=ch; st[m+1]='\000';
        if(pd==1) Insert(root,st,ii); else
        if(pd==2) Erase(root,st); else
        if(pd==3){
            int t1=getint(),t2=getint(),t3=getint();
            if(lstans==-1) lstans=1;
            printf("%lld\n",lstans=Query(root,st,(lstans*t1+t2)%t3+1));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值