题解 s2mple

传送门

发现这个“在每个本质不同子串中的出现次数十分阴间”
我们可以做一个神奇的转化:
这个东西就是在原串的前后添加一些字符,使得到的串是 \(s\) 的子串的方案数
正确性显然,就是在拼产生贡献的本质不同子串

那么这个问题就比较好解决了
在前面加字符就是 parent 树的子树内的节点
在后面加字符可以在 SAM 形成的 DAG 上做 DP
那么倍增找到 \(s_{l, r}\) 对应的节点统计答案即可
复杂度 \(O(n\log n)\)

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

int n, q;
char s[N];

namespace force{
	ull h[N], pw[N];
	bool vis[2010][2010];
	const ull base=13131;
	unordered_map<ull, bool> mp;
	inline ull hashing(int l, int r) {return h[r]-h[l-1]*pw[r-l+1];}
	void solve() {
		pw[0]=1;
		for (int i=1; i<=n; ++i) pw[i]=pw[i-1]*base;
		for (int i=1; i<=n; ++i) {
			ull h=0;
			for (int j=i; j<=n; ++j) {
				h=h*base+s[j];
				if (mp.find(h)==mp.end()) vis[i][j]=mp[h]=1;
			}
		}
		for (int i=1,l,r; i<=q; ++i) {
			scanf("%d%d", &l, &r);
			ull tem=0; ll ans=0;
			for (int j=l; j<=r; ++j) tem=tem*base+s[j];
			for (int j=1; j<=n; ++j) {
				h[j-1]=0; int cnt=0;
				for (int k=j; k<=n; ++k) {
					h[k]=h[k-1]*base+s[k];
					if (k-j>=r-l && hashing(k-(r-l), k)==tem) ++cnt;
					if (vis[j][k]) ans+=cnt;
				}
			}
			printf("%lld\n", ans);
		}
	}
}

namespace task1{
	bool vis[N];
	ll suf[N], ans[N], dp[N];
	vector<pair<int, int>> que[N];
	int fail[N], len[N], tr[N][26], cnt[N], tem[N], dep[N], fa[21][N], lg[N], now, tot;
	void init() {fail[now=tot=0]=-1;}
	void ins(char c) {
		c-='a';
		int cur=++tot;
		len[cur]=len[now]+1;
		int p, q;
		for (p=now; ~p&&!tr[p][c]; tr[p][c]=cur,p=fail[p]);
		if (p==-1) fail[cur]=0;
		else if (len[q=tr[p][c]]==len[p]+1) fail[cur]=q;
		else {
			int cln=++tot;
			len[cln]=len[p]+1;
			fail[cln]=fail[q];
			for (int i=0; i<26; ++i) tr[cln][i]=tr[q][i];
			for (; ~p&&tr[p][c]==q; tr[p][c]=cln,p=fail[p]);
			fail[cur]=fail[q]=cln;
		}
		vis[now=cur]=1;
	}
	void solve() {
		init();
		for (int i=1; i<=n; ++i) ins(s[i]);
		// cout<<"fail: "; for (int i=1; i<=tot; ++i) cout<<fail[i]<<' '; cout<<endl;
		// cout<<"len: "; for (int i=1; i<=tot; ++i) cout<<len[i]<<' '; cout<<endl;
		for (int i=1; i<=tot; ++i) ++cnt[len[i]];
		for (int i=1; i<=n; ++i) cnt[i]+=cnt[i-1];
		for (int i=1; i<=tot; ++i) tem[cnt[len[i]]--]=i;
		for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
		for (int i=tot,u; i; --i) {
			++suf[u=tem[i]];
			for (int j=0; j<26; ++j) if (tr[u][j]) suf[u]+=suf[tr[u][j]];
			// if (vis[u]) suf[u]=dp[u];
			// suf[fail[u]]+=suf[u];
			dp[fail[u]]+=dp[u]+(len[u]-len[fail[u]])*suf[u];
		}
		for (int i=1; i<=tot; ++i) dep[tem[i]]=dep[fail[tem[i]]]+1;
		for (int i=1,u; i<=tot; ++i) {
			u=tem[i];
			dep[u]=dep[fa[0][u]=fail[u]]+1;
			for (int i=1; dep[u]>=1<<i; ++i) fa[i][u]=fa[i-1][fa[i-1][u]];
		}
		// cout<<"dp: "; for (int i=1; i<=tot; ++i) cout<<dp[i]<<' '; cout<<endl;
		// cout<<"suf: "; for (int i=1; i<=tot; ++i) cout<<suf[i]<<' '; cout<<endl;
		for (int i=1,l,r; i<=q; ++i) {
			scanf("%d%d", &l, &r);
			que[r].pb({l, i});
		}
		for (int i=1,now=0; i<=n; ++i) {
			now=tr[now][s[i]-'a'];
			for (auto it:que[i]) {
				int u=now;
				for (int j=lg[dep[u]]-1; ~j; --j)
					if (len[fa[j][u]]>=i-it.fir+1)
						u=fa[j][u];
				ans[it.sec]=dp[u]+(len[u]-(i-it.fir))*suf[u];
			}
		}
		for (int i=1; i<=q; ++i) printf("%lld\n", ans[i]);
	}
}

signed main()
{
	freopen("s2mple.in", "r", stdin);
	freopen("s2mple.out", "w", stdout);

	scanf("%d%d%s", &n, &q, s+1);
	// force::solve();
	task1::solve();

	return 0;
}
posted @ 2022-05-30 21:54  Administrator-09  阅读(3)  评论(0编辑  收藏  举报