Educational Codeforces Round 71 G. Indie Album

链接

点击跳转

题解

群大佬太强了

这题的做法是离线,把查询串建成 A C AC AC自动机,然后对给定的字典树进行 d f s dfs dfs,同时在自动机上跑,字典树上每个点都对应跑到 A C AC AC自动机上某个点 p p p,那么显然此时 p p p以及 p p p f a i l fail fail树上的所有祖先都匹配到了,最直接的想法是把从 p p p到根节点上的权值都 + 1 +1 +1,这个看起来像是 l c t lct lct或者树链剖分;但其实 p p p影响到的也只有 p p p f a i l fail fail树上的祖先,因此我直接给 p p p的权值 + 1 +1 +1,查询的时候查询子树和就行了

代码

#include <bits/stdc++.h>
#define eps 1e-8
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define cl(x) memset(x,0,sizeof(x))
#define mod 1073741824ll
#define maxn 400010
#define maxe 400010
using namespace std;
typedef long long ll;
struct Graph
{
	int etot, head[maxn], to[maxe], next[maxe];
	void adde(int a, int b)
	{to[++etot]=b;next[etot]=head[a];head[a]=etot;}
}G;
struct ACautomaton
{
	int trie[maxn][30], tot, fail[maxn];
	void clear(){tot=1;}
	int insert(char *r, int len)
	{
		auto pos=1;
		for(auto i=1;i<=len;i++)
			pos = trie[pos][r[i]] ? trie[pos][r[i]] : trie[pos][r[i]]=++tot;
		return pos;
	}
	int append(int pos, int x)
	{
		if(trie[pos][x])return trie[pos][x];
		else return trie[pos][x]=++tot;
	}
	void build()
	{
		queue<int> q;
		int u, v, f;
		q.push(1);
		while(!q.empty())
		{
			u=q.front(); q.pop();
			for(auto i=1;i<=26;i++)
				if(trie[u][i])
				{
					v=trie[u][i];
					for(f=fail[u];f and !trie[f][i];f=fail[f]);
					fail[v] = f ? trie[f][i] : 1;
					q.push(v);
				}
		}
	}
	int move(int pos, int c)
	{
		for(;pos and !trie[pos][c];pos=fail[pos]);
		return pos ? trie[pos][c] : 1;
	}
}trie, aca;
struct BIT
{
	int bit[maxn], n;
	void init(int N){n=N;}
	int lowbit(int x){return x&-x;}
	void add(int pos, int v){for(;pos<=n;pos+=lowbit(pos))bit[pos]+=v;}
	int sum(int pos)
	{
		int ans=0;
		for(;pos;pos-=lowbit(pos))ans+=bit[pos];
		return ans;
	}
}bit;
int tid[maxn], ltid[maxn], rtid[maxn], tim;
void dfs(int pos)
{
	tid[pos]=ltid[pos]=++tim;
	for(auto p=G.head[pos];p;p=G.next[p])
		dfs(G.to[p]);
	rtid[pos]=tim;
}
int ans[maxn], aca_endp[maxn], trie_endp[maxn];
vector<int> q[maxn];
void solve(int pos, int aca_pos)
{
	bit.add(tid[aca_pos],+1);
	for(auto x:q[pos])
		ans[x] = bit.sum(rtid[aca_endp[x]]) - bit.sum(ltid[aca_endp[x]]-1);
	for(int i=1;i<=26;i++)
		if(trie.trie[pos][i])
			solve(trie.trie[pos][i],aca.move(aca_pos,i));
	bit.add(tid[aca_pos],-1);
}
char s[maxn];
int main()
{
	ios::sync_with_stdio(false);
	int i, j, n, m, type;
	char c;
	trie.clear();
	aca.clear();
	cin>>n;
	for(i=1;i<=n;i++)
	{
		cin>>type;
		if(type==1)
		{
			cin>>c;
			trie_endp[i]=trie.append(1,int(c-'a'+1));
		}
		else
		{
			cin>>j>>c;
			trie_endp[i]=trie.append(trie_endp[j],int(c-'a'+1));
		}
	}
 
	cin>>m;
	for(i=1;i<=m;i++)
	{
		cin>>j>>s+1;
		int len=strlen(s+1);
		for(int j=1;j<=len;j++)s[j]=s[j]-'a'+1;
		aca_endp[i] = aca.insert(s,len);
		q[trie_endp[j]].push_back(i);
	}
 
	aca.build();
	for(i=2;i<=aca.tot;i++)G.adde(aca.fail[i],i);
	dfs(1);
 
	bit.init(tim);
	solve(1,1);
	for(i=1;i<=m;i++)cout<<ans[i]<<'\n';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值