Salieri
考虑建立\(AC\)自动机。
那考虑如何快速求出第\(k\)的权值。
考虑我们无法直接就维护这种东西,这太神秘。
所以我们不妨考虑二分变成数数问题,那就看起来很不错。
在之前的文章中指出过“AC自动机”的 \(fail\) 树具有后缀子串性质,所以如果我们 在\(T\) 在tire上遍历的时候打标记,那一个子串出现的次数就是其子树内的和。
那么考虑如何加速这个过程,我们发现的一个关键性质是\(\sum|T|\)很小,所以我们打的 \(tag\) 的次数也很小,那么我们发现,如果我们对于操作的关键点建虚树,其等价的区间数量很少。
启发我们在等价区间上操作即可,那我们等价于在一段直链上的\(\sum [v_i > w]v_i\),可以使用主席树维护。
Salieri
// code by fhq_treap
#include<bits/stdc++.h>
#define ll long long
#define N 300005
inline ll read(){
char C=getchar();
ll A=0 , F=1;
while(('0' > C || C > '9') && (C != '-')) C=getchar();
if(C == '-') F=-1 , C=getchar();
while('0' <= C && C <= '9') A=(A << 1)+(A << 3)+(C - 48) , C=getchar();
return A*F;
}
template <typename T>
void write(T x)
{
if(x < 0) {
putchar('-');
x = -x;
}
if(x > 9)
write(x/10);
putchar(x % 10 + '0');
return;
}
int ch[N][4];
int fail[N];
char s[N];
int cnt;
using std::vector;
vector<int>key;
inline void insert(){
int u = 0;
int len = strlen(s + 1);
for(int i = 1;i <= len;++i){
if(!ch[u][s[i] - 'a'])
ch[u][s[i] - 'a'] = ++ cnt;
// std::cout<<u<<" "<<ch[u][s[i] - 'a']<<std::endl;
u = ch[u][s[i] - 'a'];
}
key.push_back(u);
}
using std::queue;
queue<int>Q;
vector<int>T[N],val[N];
inline void build(){
for(int i = 0;i < 4;++i){
if(ch[0][i])
fail[ch[0][i]] = 0,Q.push(ch[0][i]);
}
while(Q.size()){
int u = Q.front();
Q.pop();
for(int i = 0;i < 4;++i){
if(ch[u][i]){
fail[ch[u][i]] = ch[fail[u]][i],Q.push(ch[u][i]);
}else{
ch[u][i] = ch[fail[u]][i];
}
}
}
for(int i = 1;i <= cnt;++i)
T[fail[i]].push_back(i);
}
//AC自动机
int head[N];
struct P{
int ls,rs;
int cnt;
}Seg[N * 40];
#define ls(x) Seg[x].ls
#define rs(x) Seg[x].rs
#define c(x) Seg[x].cnt
#define mid ((l + r) >> 1)
#define inf 10000
int scnt;
inline void change(int las,int &now,int l,int r,int p){
if(!now)now = ++scnt;
// std::cout<<las<<" "<<now<<" "<<l<<" "<<r<<" "<<p<<std::endl;
Seg[now] = Seg[las];
c(now) ++ ;
if(l == r)return ;
if(p <= mid){
ls(now) = 0;
change(ls(las),ls(now),l,mid,p);
}
if(p > mid){
rs(now) = 0;
change(rs(las),rs(now),mid + 1,r,p);
}
}
int dfn[N];
int dfncnt;
int fa[N][20];
int dep[N];
inline void dfs(int u){
// std::cout<<u<<" "<<val[u]<<std::endl;
dfn[u] = ++dfncnt;
if(u){
fa[u][0] = fail[u];
dep[u] = dep[fa[u][0]] + 1;
for(int i = 1;i <= 19;++i)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
if(val[u].size()){
int lst = head[fail[u]];
for(int j = 0;j < val[u].size();++j)
change(lst,head[u],1,inf,val[u][j]),lst = head[u],head[u] = 0;
head[u] = lst;
}else{
head[u] = head[fail[u]];
}
}
for(int i = 0;i < T[u].size();++i){
int v = T[u][i];
dfs(v);
}
}
inline int lca(int x,int y){
if(dep[x] < dep[y])
std::swap(x,y);
for(int i = 19;i >= 0;--i){
if(dep[fa[x][i]] > dep[y])
x = fa[x][i];
}
if(dep[x] > dep[y])
x = fa[x][0];
if(x == y)
return x;
for(int i = 19;i >= 0;--i){
if(fa[x][i] != fa[y][i])
x = fa[x][i],y = fa[y][i];
}
return fa[x][0];
}
inline int find(int las,int now,int l,int r,int lim){
// std::cout<<"find "<<las<<" "<<now<<" "<<l<<" "<<r<<" "<<lim<<std::endl;
if(las + now == 0 || r < lim)return 0;
if(l >= lim)return c(now) - c(las);
return find(ls(las),ls(now),l,mid,lim) + find(rs(las),rs(now),mid + 1,r,lim);
}
//主席树
vector<int>point;
bool cmp(int a,int b){
return dfn[a] < dfn[b];
}
vector<int>F[N];
int ccnt[N],fi[N];
inline void solve(int u){
for(int i = 0;i < F[u].size();++i){
int v = F[u][i];
solve(v);
ccnt[u] += ccnt[v];
}
// std::cout<<u<<" "<<ccnt[u]<<"\n";
}
inline void clear(int u){
fi[u] = 0;
ccnt[u] = 0;
for(int i = 0;i < F[u].size();++i){
int v = F[u][i];
clear(v);
}
F[u].clear();
}
inline int check(int val){
// puts("CHECK");
int sum = 0;
for(int i = 0;i < point.size();++i){
int u = point[i];
if(u){
int lim = ceil(1.0 * val / (1.0 * ccnt[u]));
// std::cout<<u<<" "<<fi[u]<<" "<<ccnt[u]<<" "<<lim<<" "<<find(head[fi[u]],head[u],1,inf,lim)<<std::endl;
sum = sum + find(head[fi[u]],head[u],1,inf,lim);
}
}
return sum;
}
#define INF 5e8
int k;
inline void rebuild(){
point.push_back(0);
std::sort(point.begin(),point.end(),cmp);
point.erase(std::unique(point.begin(),point.end()),point.end());
int len = point.size();
for(int i = 1;i < len;++i)
point.push_back(lca(point[i],point[i - 1]));
std::sort(point.begin(),point.end(),cmp);
point.erase(std::unique(point.begin(),point.end()),point.end());
// for(int i = 0;i < point.size();++i)
// std::cout<<point[i]<<" ";
// puts("");
for(int i = 1;i < point.size();++i){
int LI = lca(point[i],point[i - 1]);
// std::cout<<LI<<" "<<point[i]<<"\n";
F[LI].push_back(point[i]);
fi[point[i]] = LI;
}
solve(0);
int l = 0,r = INF;
#define mid ((l + r) >> 1)
int ans = 0;
while(l <= r){
int q ;
// std::cout<<l<<" "<<r<<" "<<mid<<" "<<()<<"\n";
q = check(mid);
if(q >= k){
ans = mid;
l = mid + 1;
}else
r = mid - 1;
}
std::cout<<ans<<"\n";
clear(0);
}
//虚树
int n,m;
int main(){
// freopen("fuck.out","w",stdout);
key.push_back(0);
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i){
scanf("%s",s + 1);
insert();
int vi = read();
val[key[key.size() - 1]].push_back(vi);
// std::cout<<key[key.size() - 1]<<" "<<vi<<"\n"<<std::endl;
}
// for(int i = 1;i < key.size();++i)
// std::cout<<key[i]<<" ";
// puts("");
build();
// for(int i = 1;i <= cnt;++i)
// std::cout<<i<<" "<<fail[i]<<"\n";
// puts("");
dfs(0);
while(m -- ){
scanf("%s",s + 1);
int len = strlen(s + 1);
scanf("%d",&k);
point.clear();
int u = 0;
for(int i = 1;i <= len;++i){
u = ch[u][s[i] - 'a'];
ccnt[u] ++ ;
point.push_back(u);
}
// for(int i = 0;i < point.size();++i){
// std::cout<<point[i]<<" ";
// }
// puts("");
rebuild();//虚树
}
}
/*
15 4
ba 18
cbc 74
aac 54
ba 77
a 66
c 96
cdb 47
dc 45
cb 62
db 88
dda 93
db 34
b 81
acd 100
da 80
abdbaca 5
*/