• 51nod 麦克打电话(AC自动机+树状数组)


    SAM+线段树合并的裸题。
    但我们讨论AC自动机的做法。
    先建出AC自动机。考虑询问在[a,b]中出现的次数就是([1,b])的出现次数-([1,a-1])的出现次数。把询问离线。然后我们要求的就是第i个字符串在([1,x])中出现次数。我们在从([1,x-1])([1,x])的过程中把(S_x)放到AC自动机上跑,跑到的每一个节点都加1。然后询问就是在(S_i)在fail树上对应位置求一个子树和这个用树状数组维护就行。因为fail树上每一个节点的后代都包含这个节点。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<vector>
    using namespace std;
    const int N=301000;
    int n,m,ans[501000];
    string s[N];
    int cnt,head[N];
    struct edge{
    	int to,nxt;
    }e[N];
    void add(int u,int v){
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    struct ques{
    	int x,k,id;
    	ques(int xx=0,int kk=0,int idx=0){
    		x=xx;k=kk;id=idx;
    	}
    };
    vector<ques> vec[N];
    struct Tree{
    	int dep[N],size[N],dfn[N],tot;
    	int tr[N];
    	void dfs(int u,int f){
    		dep[u]=dep[f]+1;
    		dfn[u]=++tot;
    		size[u]=1;
    		int maxson=-1;
    		for(int i=head[u];i;i=e[i].nxt){
    			int v=e[i].to;
    			dfs(v,u);
    			size[u]+=size[v];
    		}
    	}
    	int lowbit(int x){
    		return x&-x;
    	}
    	void add(int x){
    		for(int i=x;i<=tot;i+=lowbit(i))tr[i]++;
    	}
    	int getsum(int x){
    		int tmp=0;
    		for(int i=x;i;i-=lowbit(i))tmp+=tr[i];
    		return tmp;
    	}
    }tree;
    bool cmp(int x,int y){
    	if(tree.dfn[x]<tree.dfn[y])return true;
    	else return false;
    }
    struct AC{
    	int trans[N][27],point[N],tot,fail[N];
    	void ins(string s,int k){
    		int now=0;
    		int len=s.size();
    		for(int i=0;i<len;++i){
    			if(trans[now][s[i]-'a'+1]==0)trans[now][s[i]-'a'+1]=++tot;
    			now=trans[now][s[i]-'a'+1];
    		}
    		point[k]=now;
    	}
    	void get_fail(){
    		queue<int> q;
    		for(int i=1;i<=26;++i)if(trans[0][i])q.push(trans[0][i]);
    		while(!q.empty()){
    			int now=q.front();
    			q.pop();
    			for(int i=1;i<=26;++i)
    				if(trans[now][i])fail[trans[now][i]]=trans[fail[now]][i],q.push(trans[now][i]);
    				else trans[now][i]=trans[fail[now]][i];
    		}
    	}
    	void work(string s){
    		int now=0;
    		int len=s.size();;
    		tree.add(tree.dfn[0]);
    		for(int i=0;i<len;++i){
    			now=trans[now][s[i]-'a'+1];
    			tree.add(tree.dfn[now]);
    		}
    	}
    }ac;
    int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return sum*f;
    }
    int main(){
    	n=read();m=read();
    	for(int i=1;i<=n;++i)cin>>s[i],ac.ins(s[i],i);
    	int a,b,c;
    	for(int i=1;i<=m;++i){
    		a=read();b=read();c=read();
    		vec[a-1].push_back(ques(ac.point[c],-1,i));
    		vec[b].push_back(ques(ac.point[c],1,i));
    	}
    	ac.get_fail();
    	for(int i=1;i<=ac.tot;++i)add(ac.fail[i],i);
    	tree.dfs(0,0);
    	for(int i=1;i<=n;++i){
    		ac.work(s[i]);
    		for(int j=0;j<vec[i].size();++j){
    			int L=tree.dfn[vec[i][j].x];
    			int R=tree.dfn[vec[i][j].x]+tree.size[vec[i][j].x]-1;
    			ans[vec[i][j].id]+=vec[i][j].k*(tree.getsum(R)-tree.getsum(L-1));
    		}
    	}
    	for(int i=1;i<=m;++i)printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    页面跳转刷新
    表格表头绘制对角线(不固定表格宽高)
    发送邮件的工具类
    重写equals()和hashCode()
    设计模式--原型模式[转载]
    设计模式--外观模式
    设计模式--代理模式
    js处理json js递归
    MySQL锁详解
    开发一个微信小程序实例教程
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/10235752.html
Copyright © 2020-2023  润新知