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
*/
posted @ 2022-02-12 20:56  fhq_treap  阅读(109)  评论(0编辑  收藏  举报