zoukankan      html  css  js  c++  java
  • 【CF587F】Duff is Mad(AC自动机+根号分治)

    点此看题面

    • 给定(n)个字符串,(q)次询问,每次求(s_{lsim r})(s_k)中的出现次数总和。
    • (n,q,sum|s|le10^5)

    (AC)自动机+根号分治

    由于询问的若干串之间不相干,显然可以把询问拆成用(s_{1sim r})的答案减去(s_{1,l-1})的答案,然后就变成了每次询问前(i)个串在(s_k)中的出现次数。

    而众所周知,在(AC)自动机上,子串是前缀的后缀,前缀就是根到(s_k)路径上的每个节点,后缀就是一个节点的所有祖先。反过来也可以看作是根到(s_k)路径上的点在询问串对应的子树内。

    考虑(|s_k|),把情况分成小于等于(sqrt N)和大于(sqrt N)两类。

    如果(|s_k|lesqrt N),我们直接枚举把询问的(s_k)扔到(l-1)(r)两个端点上,然后只要枚举每个串在(fail)树上给子树打标记(转化成(dfs)序列后用树状数组),然后处理对应端点上的询问求到(s_k)路径上的所有点的标记和即可。

    如果(|s_k|>sqrt N),由于这样的串不超过(sqrt N)个,我们把询问扔给(s_k),对于每个这样的(s_k)分别求解答案。具体地,我们只要给到(s_k)路径上的所有点打上标记,枚举每个串求出子树内的标记和,然后前缀和+差分即可。

    注意到这里的树状数组可以用分块替代消去(log),但实际上没啥意义,估计树状数组小常数跑得更快。

    代码:(O(nsqrt nlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define LL long long
    using namespace std;
    int n,Nt=1,sz,id[N+5],bg[N+5],l[N+5];LL v[N+5],ans[N+5];char s[N+5];
    struct Q {int p,x,y;I Q(CI i=0,CI a=0,CI b=0):p(i),x(a),y(b){}};vector<Q> V[N+5],G[N+5];vector<Q>::iterator it;
    namespace AC//AC自动机
    {
    	#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    	struct node {int F,S[30];}O[N+5];
    	int d,dI[N+5],dO[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N+5];
    	I void Init(CI x=1) {dI[x]=++d;for(RI i=lnk[x];i;i=e[i].nxt) Init(e[i].to);dO[x]=d;}//处理dfs序
    	struct TreeArray
    	{
    		int a[N+5];I void U(RI x,CI v) {W(x<=Nt) a[x]+=v,x+=x&-x;}//单点修改/后缀修改
    		I int Q(RI x,RI t=0) {W(x) t+=a[x],x-=x&-x;return t;}//前缀询问/单点询问
    	}A;
    	I int Ins(char* s,CI l)//插入一个串
    	{
    		RI x=1;for(RI i=1,t;i<=l;++i) !O[x].S[t=s[i]&31]&&(O[x].S[t]=++Nt),x=O[x].S[t];return x;//返回节点编号
    	}
    	int q[N+5];I void Build()//建AC自动机
    	{
    		RI i,k,H=1,T=0;for(i=1;i<=26;++i) (O[1].S[i]?O[q[++T]=O[1].S[i]].F:O[1].S[i])=1;
    		W(H<=T) for(k=q[H++],i=1;i<=26;++i) (O[k].S[i]?O[q[++T]=O[k].S[i]].F:O[k].S[i])=O[O[k].F].S[i];
    		for(i=2;i<=Nt;++i) add(O[i].F,i);Init();//建fail树
    	}
    	I void P(char* s,CI l,CI v) {for(RI i=1,x=1;i<=l;++i) x=O[x].S[s[i]&31],A.U(dI[x],v);}//给路径上所有点打标记
    	I int G(CI x) {return A.Q(dO[x])-A.Q(dI[x]-1);}//询问子树标记和
    	I void U(CI x) {A.U(dI[x],1),A.U(dO[x]+1,-1);}//给子树打标记
    	I LL Q(char* s,CI l) {LL t=0;for(RI i=1,x=1;i<=l;++i) x=O[x].S[s[i]&31],t+=A.Q(dI[x]);return t;}//求路径上所有点标记和
    }
    int main()
    {
    	RI Qt,i,j;for(scanf("%d%d",&n,&Qt),i=1;i<=n;++i)
    		bg[i]=bg[i-1]+l[i-1],scanf("%s",s+bg[i]+1),id[i]=AC::Ins(s+bg[i],l[i]=strlen(s+bg[i]+1));
    	RI x,y,k;for(sz=sqrt(n),i=1;i<=Qt;++i) scanf("%d%d%d",&x,&y,&k),
    		l[k]<=sz?(V[x-1].push_back(Q(i,k,-1)),V[y].push_back(Q(i,k,1))):G[k].push_back(Q(i,x,y));//根号分治
    	for(AC::Build(),i=1;i<=n;++i) if(!G[i].empty())//对于长度超过sqrt(n)的串
    	{
    		for(AC::P(s+bg[i],l[i],1),j=1;j<=n;++j) v[j]=v[j-1]+AC::G(id[j]);//前缀和
    		for(it=G[i].begin();it!=G[i].end();++it) ans[it->p]=v[it->y]-v[it->x-1];AC::P(s+bg[i],l[i],-1);//差分回应询问
    	}
    	for(i=1;i<=n;++i)//对于长度不超过sqrt(n)的串
    	{
    		for(AC::U(id[i]),it=V[i].begin();it!=V[i].end();++it) ans[it->p]+=it->y*AC::Q(s+bg[it->x],l[it->x]);//加入每个前缀,枚举端点上的询问
    	}
    	for(i=1;i<=Qt;++i) printf("%lld
    ",ans[i]);return 0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    CS229 6.4 Neurons Networks Autoencoders and Sparsity
    CS229 6.3 Neurons Networks Gradient Checking
    【Leetcode】【Easy】Min Stack
    【Leetcode】【Easy】Merge Sorted Array
    【Leetcode】【Easy】ZigZag Conversion
    【Leetcode】【Easy】Valid Palindrome
    【Leetcode】【Easy】Reverse Integer
    【Leetcode】【Easy】Palindrome Number
    【Leetcode】【Easy】Length of Last Word
    【Leetcode】【Easy】Remove Nth Node From End of List
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF587F.html
Copyright © 2011-2022 走看看