题解 [CF547E] Mike and Friends

传送门

把 call 函数的定义读反了,自闭了一年

读反了其实也是可以做的
那么问题变为:给定串 s1sn,对于一个 tslsr 作为子串在 t 中出现的次数
做法 1:
因为我啥也不会嘛
s 建广义 SAM,考虑子串是前缀的后缀
那么把 t 在上面跑,对于每个前缀对应的节点对其到祖先的路径上 对应某个/些 si 的节点个数 求和
需要特殊处理这个前缀对应的节点对应某个/些 si 的情况
对于 [l,r] 的限制应该可以主席树
所谓“特殊处理”:没有 [l,r] 限制应该就是对每个节点开 set 存对应的 s 的长度,查询时二分 |t|
加上限制就是各种二维偏序了
upd:哦我好像傻了,每个节点貌似最多对应一个 si
做法 2:
这个“特殊处理”很烦,设法去掉它
那么对 t 建 SAM,把所有 s 拿来在上面跑
每个 si 对应的节点产生的贡献是这个节点 endpos 集合的大小
加限制的话应该可以差分,跑 sl 前加入 t,跑完 sr 删除 t

回到原题:
现在求包含 t 的次数
发现 AC 自动机更好用,差分成 s1sl1s1sr 处理
si 的每个前缀对对应节点产生 1 贡献,就是求 fail 树上子树贡献和
树状数组即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long

int n, q;
char *s[N], t[N];
ll bit[N], ans[N];
int head[N], ecnt;
vector<pair<int, int>> add[N], del[N];
int tr[N][26], fail[N], siz[N], id[N], pos[N], tot, now;
struct edge{int to, next;}e[N];
inline void link(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}
inline void upd(int i, int dat) {for (; i<=now; i+=i&-i) bit[i]+=dat;}
inline ll query(int i) {ll ans=0; for (; i; i-=i&-i) ans+=bit[i]; return ans;}

int ins(char* c) {
	int p, *t;
	for (p=0; ; p=*t) {
		t=&tr[p][*c-'a'];
		if (!*t) *t=++tot;
		if (!*(++c)) return *t;
	}
}

void dfs(int u) {
	siz[u]=1;
	id[u]=++now;
	for (int i=head[u]; ~i; i=e[i].next)
		dfs(e[i].to), siz[u]+=siz[e[i].to];
}

void build() {
	int u=0;
	queue<int> q;
	for (int i=0; i<26; ++i)
		if (tr[u][i]) fail[tr[u][i]]=u, link(u, tr[u][i]), q.push(tr[u][i]);
		else tr[u][i]=u;
	while (q.size()) {
		u=q.front(); q.pop();
		for (int i=0; i<26; ++i)
			if (tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i], link(tr[fail[u]][i], tr[u][i]), q.push(tr[u][i]);
			else tr[u][i]=tr[fail[u]][i];
	}
}

void enable(char* c) {
	int p, *t;
	for (p=0; ; p=*t) {
		t=&tr[p][*c-'a'];
		upd(id[*t], 1);
		if (!*(++c)) return ;
	}
}

signed main()
{
	// cout<<double(sizeof(tr))/1000/1000<<endl;
	scanf("%d%d", &n, &q);
	memset(head, -1, sizeof(head));
	for (int i=1,len; i<=n; ++i) {
		scanf("%s", t+1);
		len=strlen(t+1);
		s[i]=new char[len+5];
		for (int j=1; j<=len+1; ++j) s[i][j]=t[j];
		pos[i]=ins(s[i]+1);
	}
	build(); dfs(0);
	for (int i=1,l,r,k; i<=q; ++i) {
		scanf("%d%d%d", &l, &r, &k);
		add[r].pb({k, i});
		del[l-1].pb({k, i});
	}
	for (int i=1; i<=n; ++i) {
		enable(s[i]+1);
		for (auto it:add[i]) ans[it.sec]+=query(id[pos[it.fir]]+siz[pos[it.fir]]-1)-query(id[pos[it.fir]]-1);
		for (auto it:del[i]) ans[it.sec]-=query(id[pos[it.fir]]+siz[pos[it.fir]]-1)-query(id[pos[it.fir]]-1);
	}
	for (int i=1; i<=q; ++i) printf("%lld\n", ans[i]);
	
	return 0;
}
posted @   Administrator-09  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示