• 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
    */
    
  • 相关阅读:
    LINQ 详解
    oracle下查询的sql已经超出IIS响应时间
    IOC应用之 Ninject
    JSONP ---------跨域
    国内各大互联网公司相关技术站点2.0版 (集合腾讯、阿里、百度、搜狐、新浪、360等共49个)
    IO多路复用,以socket为例
    socket机制下实现的多用户与服务器交互
    在一个进程中定义多个线程
    基于tcp的socketserver,即tcp的多线程
    基于upd的socketserver,即udp的多线程
  • 原文地址:https://www.cnblogs.com/dixiao/p/15887387.html
Copyright © 2020-2023  润新知