1. 程式人生 > >【CF587F】Duff is Mad AC自動機+分塊

【CF587F】Duff is Mad AC自動機+分塊

query tor 題解 upd AR div -o OS iter

【CF587F】Duff is Mad

題意:給出n個串$s_1,s_2..s_n$,有q組詢問,每次給出l,r,k,問你編號在[l,r]中的所有串在$s_k$中出現了多少次。

$\sum|s_i|,q\le 10^5$

題解:先將詢問離線,改成前綴相減。然後建出AC自動機,考慮分塊。

對於長度$>\sqrt n$的詢問串,這種串最多$\sqrt n$個,我們每次可以掃一遍整個fail樹,處理出每個節點到根的路徑上有多少個詢問串中的點。然後將所有串一個一個加入到fail樹裏,假如加入的串的結束節點到根路徑上有a個詢問串種的點,則答案+=a。

對於長度$<\sqrt n$的串,我們按編號一個一個處理。我們加入一個串時,要將其結束節點的fail樹子樹中所有節點的點權都+1。放到DFS序上就是區間+操作。我們再次分塊便可做到$O(1)-O(\sqrt n)$的復雜度。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int maxn=100010;
typedef long long ll;
int n,m,B,tot,cnt;
ll sum;
struct node
{
	int ch[26],fail;
}p[maxn];
struct query
{
	int x,k,org;
	query() {}
	query(int a,int b,int c) {x=a,k=b,org=c;}
};
int lp[maxn],rp[maxn],q[maxn],pos[maxn],head[maxn],to[maxn],nxt[maxn],p1[maxn],p2[maxn];
ll ans[maxn],s[maxn],sb[1000];
char str[maxn];
vector<query> v1[maxn],v2[maxn];
vector<query>::iterator it;
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
inline void build()
{
	int i,u,h=1,t=0;
	q[++t]=1;
	while(h<=t)
	{
		u=q[h++];
		for(i=0;i<26;i++)
		{
			if(p[u].ch[i])
			{
				q[++t]=p[u].ch[i];
				if(u==1)	p[p[u].ch[i]].fail=1;
				else	p[p[u].ch[i]].fail=p[p[u].fail].ch[i];
			}
			else
			{
				if(u==1)	p[u].ch[i]=1;
				else	p[u].ch[i]=p[p[u].fail].ch[i];
			}
		}
	}
}
bool cmp(const query &a,const query &b)
{
	return a.x<b.x;
}
inline void add(int a,int b)
{
	to[cnt]=b,nxt[cnt]=head[a],head[a]=cnt++;
}
void dfs(int x)
{
	p1[x]=++p2[0];
	for(int i=head[x];i!=-1;i=nxt[i])	dfs(to[i]);
	p2[x]=p2[0];
}
inline void upd(int a,int b)
{
	int i,c=a/B,d=b/B;
	if(c==d)	for(i=a;i<=b;i++)	s[i]++;
	else
	{
		for(i=a;i<c*B+B;i++)	s[i]++;
		for(i=d*B;i<=b;i++)	s[i]++;
		for(i=c+1;i<d;i++)	sb[i]++;
	}
}
int main()
{
	n=rd(),m=rd();
	int i,j,u,a,b,c;
	tot=1;
	for(i=1;i<=n;i++)
	{
		lp[i]=rp[i-1],scanf("%s",str+lp[i]),rp[i]=lp[i]+strlen(str+lp[i]);
		for(u=1,j=lp[i];j<rp[i];j++)
		{
			a=str[j]-‘a‘;
			if(!p[u].ch[a])	p[u].ch[a]=++tot;
			u=p[u].ch[a];
		}
		pos[i]=u;
	}
	build(),B=int(sqrt(double(tot+1)));
	for(i=1;i<=m;i++)
	{
		a=rd(),b=rd(),c=rd();
		if(rp[c]-lp[c]>B)
		{
			if(a!=1)	v2[c].push_back(query(a-1,-1,i));
			v2[c].push_back(query(b,1,i));
		}
		else
		{
			if(a!=1)	v1[a-1].push_back(query(c,-1,i));
			v1[b].push_back(query(c,1,i));
		}
	}
	memset(head,-1,sizeof(head));
	for(i=2;i<=tot;i++)	add(p[i].fail,i);
	dfs(1);
	for(i=1;i<=n;i++)
	{
		upd(p1[pos[i]],p2[pos[i]]);
		for(it=v1[i].begin();it!=v1[i].end();it++)
		{
			a=(*it).x,b=(*it).k,c=(*it).org;
			for(u=1,j=lp[a];j<rp[a];j++)
			{
				u=p[u].ch[str[j]-‘a‘];
				ans[c]+=(s[p1[u]]+sb[p1[u]/B])*b;
			}
		}
	}
	for(i=1;i<=n;i++)	if(rp[i]-lp[i]>B&&v2[i].size())
	{
		memset(s,0,sizeof(s));
		for(u=1,j=lp[i];j<rp[i];j++)	u=p[u].ch[str[j]-‘a‘],s[u]++;
		for(j=tot;j>=2;j--)	s[p[q[j]].fail]+=s[q[j]];
		sort(v2[i].begin(),v2[i].end(),cmp);
		for(sum=0,j=1,it=v2[i].begin();it!=v2[i].end();it++)
		{
			a=(*it).x,b=(*it).k,c=(*it).org;
			while(j<=a)	sum+=s[pos[j++]];
			ans[c]+=b*sum;
		}
	}
	for(i=1;i<=m;i++)	printf("%lld\n",ans[i]);
	return 0;
}//

【CF587F】Duff is Mad AC自動機+分塊