• CF666E Forensic Examination 广义SAM、线段树合并、倍增、扫描线


    传送门


    朴素想法:对(M)个匹配串(T_1,...,T_M)建立广义SAM,对于每一次询问,找到这个SAM上(S[pl...pr])对应的状态,然后计算出对于每一个(i in [l,r]),计算出(T_i)能够转移到这个状态的次数然后取(max)

    需要解决两个问题:

    1、如何快速找到(S[pl...pr])在SAM上对应的状态。

    因为题目没有给(sum pr - pl)的条件,所以不能暴力,考虑一个一个字符加进去,每加入一个字符计算对应答案。将询问按照(pr)从小到大排序然后扫描线:把模板串放在广义SAM上匹配。设插入第(i)个字符时到达的状态为(u)、匹配长度为(len),那么对于某个询问(S[x...i]),若(len < x - i + 1)则次数为(0)

    (len geq x - i + 1)时不代表可以在(u)上计算答案,可能(u)状态包含的串中不包含(S[x...i])。此时还要跳(father)直到(Shortest_u leq x - i + 1),状态(u)才是需要计算的状态。这里可以倍增优化跳的过程。

    2、如何快速计算(T_i)转移到状态(u)的次数。

    可以发现这个跟计算(endpos)集合有些类似,即如果某个状态(u)被标记为“串(T_i)可以走到”,那么它的祖先也一定会有这个标记。故每个节点维护一棵线段树记录每一个串在该状态的标记次数,使用线段树合并把(parent)树上所有节点的线段树处理出来,就可以每一次(O(logn))地询问。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<ctime>
    #include<cctype>
    #include<vector>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #include<iomanip>
    #define PII pair < int , int >
    #define st first
    #define nd second
    //This code is written by Itst
    using namespace std;
    
    const int MAXN = 5e5 + 7 , MAXL = 1e5 + 7;
    char s[MAXL] , S[MAXN];
    int M , Q , cnt = 1 , ch[MAXL][26] , dep[MAXL] , ind[MAXL];
    vector < int > bel[MAXL];
    struct Query{
    	int a , b , c , d , ind;
    	bool operator <(const Query a)const{return d < a.d;}
    };
    vector < Query > que;
    PII ans[MAXN];
    
    namespace Tree{
    	struct node{
    		int l , r , maxN , sz , ind;
    	}Tree[MAXL * 60];
    	int rt[MAXL] , cnt = 1;
    	
    #define lch (Tree[x].l)
    #define rch (Tree[x].r)
    #define mid ((l + r) >> 1)
    	inline void pushup(int x){
    		if(Tree[rch].maxN > Tree[lch].maxN){
    			Tree[x].maxN = Tree[rch].maxN;
    			Tree[x].ind = Tree[rch].ind;
    		}
    		else{
    			Tree[x].maxN = Tree[lch].maxN;
    			Tree[x].ind = Tree[lch].ind;
    		}
    	}
    	
    	int insert(int x , int l , int r , int tar){
    		if(!x) x = ++cnt;
    		++Tree[x].sz;
    		if(l == r){
    			Tree[x].maxN = Tree[x].sz;
    			Tree[x].ind = l;
    			return x;
    		}
    		if(mid >= tar) lch = insert(lch , l , mid , tar);
    		else rch = insert(rch , mid + 1 , r , tar);
    		pushup(x);
    		return x;
    	}
    
    	PII query(int x , int l , int r , int L , int R){
    		if(l >= L && r <= R)
    			return PII(Tree[x].maxN , -Tree[x].ind);
    		PII sum(0 , 0);
    		if(mid >= L)
    			sum = max(sum , query(lch , l , mid , L , R));
    		if(mid < R)
    			sum = max(sum , query(rch , mid + 1 , r , L , R));
    		return sum;
    	}
    
    	int merge(int p , int q , int l , int r){
    		if(!p || !q) return p + q;
    		int t = ++cnt;
    		Tree[t].sz = Tree[p].sz + Tree[q].sz;
    		if(l == r){
    			Tree[t].maxN = Tree[t].sz;
    			Tree[t].ind = l;
    		}
    		else{
    			Tree[t].l = merge(Tree[p].l , Tree[q].l , l , mid);
    			Tree[t].r = merge(Tree[p].r , Tree[q].r , mid + 1 , r);
    			pushup(t);
    		}
    		return t;
    	}
    }
    
    namespace SAM{
        int Lst[MAXL] , Sst[MAXL] , fa[MAXL] , trans[MAXL][26];
        int cnt = 1;
    	
        int insert(int p , int len , int x){
            int t = ++cnt; Lst[t] = len;
            while(p && !trans[p][x]){
                trans[p][x] = t;
                p = fa[p];
            }
            if(!p){
                Sst[t] = fa[t] = 1;
                return t;
            }
            int q = trans[p][x];
            Sst[t] = Lst[p] + 2;
            if(Lst[q] == Lst[p] + 1){
                fa[t] = q;
                return t;
            }
            int k = ++cnt;
            memcpy(trans[k] , trans[q] , sizeof(trans[k]));
            Lst[k] = Lst[p] + 1; Sst[k] = Sst[q];
            Sst[q] = Lst[p] + 2;
            fa[k] = fa[q]; fa[q] = fa[t] = k;
            while(trans[p][x] == q){
                trans[p][x] = k;
                p = fa[p];
            }
    		return t;
        }
    
    	vector < int > ch[MAXL] , bel[MAXL];
    	int jump[MAXL][20];
    	
    	void dfs(int x){
    		for(auto &t : bel[x])
    			Tree::rt[x] = Tree::insert(Tree::rt[x] , 1 , M , t);
    		for(int i = 1 ; i <= 18 ; ++i)
    			jump[x][i] = jump[jump[x][i - 1]][i - 1];
    		
    		for(auto &t : ch[x]){
    			jump[t][0] = x;
    			dfs(t);
    			Tree::rt[x] = Tree::merge(Tree::rt[x] , Tree::rt[t] , 1 , M);
    		}
    	}
    	
    	void build(){
    		for(int i = 2 ; i <= cnt ; ++i)
    			ch[fa[i]].push_back(i);
    		dfs(1);
    	}
    
    	int calc(int x , int l){
    		if(Sst[x] <= l) return x;
    		for(int i = 18 ; i >= 0 ; --i)
    			if(Sst[jump[x][i]] > l)
    				x = jump[x][i];
    		return jump[x][0];
    	}
    }
    
    void insert(int ind){
    	scanf("%s" , s + 1);
    	int L = strlen(s + 1) , cur = 1;
    	for(int i = 1 ; i <= L ; ++i){
    		if(!ch[cur][s[i] - 'a'])
    			dep[ch[cur][s[i] - 'a'] = ++cnt] = dep[cur] + 1;
    		bel[cur = ch[cur][s[i] - 'a']].push_back(ind);
    	}
    }
    
    void build(){
    	queue < int > q;
    	q.push(ind[1] = 1);
    	while(!q.empty()){
    		int t = q.front();
    		q.pop();
    		SAM::bel[ind[t]] = bel[t];
    		for(int i = 0 ; i < 26 ; ++i)
    			if(ch[t][i]){
    				ind[ch[t][i]] = SAM::insert(ind[t] , dep[ch[t][i]] , i);
    				q.push(ch[t][i]);
    			}
    	}
    	SAM::build();
    }
    
    int main(){
        #ifndef ONLINE_JUDGE
        freopen("in" , "r" , stdin);
        //freopen("out" , "w" , stdout);
        #endif
    	scanf("%s %d" , S + 1 , &M);
    	for(int i = 1 ; i <= M ; ++i)
    		insert(i);
    	build();
    	scanf("%d" , &Q);
    	for(int i = 1 ; i <= Q ; ++i){
    		Query now;
    		scanf("%d %d %d %d" , &now.a , &now.b , &now.c , &now.d);
    		now.ind = i;
    		que.push_back(now);
    	}
    	sort(que.begin() , que.end());
    	int bef = 0 , u = 1 , len = 0;
    	for(auto &q : que){
    		while(bef < q.d){
    			++bef;
    			while(u != 1 && !SAM::trans[u][S[bef] - 'a'])
    				len = SAM::Lst[u = SAM::fa[u]];
    			if(SAM::trans[u][S[bef] - 'a']){
    				++len;
    				u = SAM::trans[u][S[bef] - 'a'];
    			}
    		}
    		if(len >= q.d - q.c + 1){
    			int t = SAM::calc(u , q.d - q.c + 1);
    			ans[q.ind] = Tree::query(Tree::rt[t] , 1 , M , q.a , q.b);
    		}
    		if(!ans[q.ind].st)
    			ans[q.ind].nd = -q.a;
    	}
    	for(int i = 1 ; i <= Q ; ++i)
    		printf("%d %d
    " , -ans[i].nd , ans[i].st);
    	return 0;
    }
    
  • 相关阅读:
    java===单类设计模式之饿汉式与懒汉式
    java===数组工具类创建,并使用eclipse导出说明文档.html
    java===static关键字
    java===this关键字
    java=====二维数组应用
    java===算法思想锻炼
    【CSP-S 2019模拟】题解
    【CSP-S 2019模拟】题解
    【LOJ#2124】【HAOI2015】—树上染色(树形dp)
    【LOJ#2019】【AHOI / HNOI2017】—影魔(线段树+扫描线)
  • 原文地址:https://www.cnblogs.com/Itst/p/10442272.html
Copyright © 2020-2023  润新知