Description
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 个小写字母
Solution
最开始以为是二分套可持久化字典树,然鹅这题有着删除这样的操作因此是不好二分的
可以直接开一棵字典树,每个节点记录以此为前缀的所有串的数量以及达到此数量的最早位置,查询的时候二分即可
那个lastans没有初值然后就调了1h,真是一个悲伤的故事
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define copy(x,t) memcpy(x,t,sizeof(x))
#define fi first
#define se second
typedef long long LL;
typedef std:: pair <int,int> pair;
const int N=100005;
char str[125];
int rec[N*60][11],tot=1;
int v[N*60],max[N*60];
std:: vector <pair> vec[N*60];
void ins(char *str,int pos,int delta) {
int len=strlen(str+1),now=1;
v[now]+=delta;
if (v[now]>max[now]) {
max[now]=v[now];
vec[now].push_back(pair(v[now],pos));
}
rep(i,1,len) {
int ch=str[i]-'a';
if (!rec[now][ch]) rec[now][ch]=++tot;
now=rec[now][ch];
v[now]+=delta;
if (v[now]>max[now]) {
max[now]=v[now];
vec[now].push_back(pair(v[now],pos));
}
}
}
int query(char *str,int u) {
int len=strlen(str+1),now=1;
rep(i,1,len) {
int ch=str[i]-'a';
now=rec[now][ch];
}
if (!now) return -1;
std:: vector <pair>:: iterator it;
it=std:: upper_bound(vec[now].begin(),vec[now].end(),pair(u+1,0));
if (it==vec[now].end()) return -1;
return (*it).se;
}
int main(void) {
int n,lastans=0; scanf("%d",&n);
rep(i,1,n) {
int opt; scanf("%d%s",&opt,str+1);
if (opt==1) {
ins(str,i,1);
} else if (opt==2) {
ins(str,i,-1);
} else {
int a,b,c; scanf("%d%d%d",&a,&b,&c);
int u=((LL)a*abs(lastans)+(LL)b)%c;
printf("%d\n", lastans=query(str,u));
}
}
return 0;
}