CF587F Duff is Mad 题解

难搞的题啊……做了一下午

使用到了主席树,AC自动机,根号分治,树状数组等算法及技巧。

可以想到一个比较明显的做法,就是建立出AC自动机,然后将fail树的dfs序处理出来,建立线段树。对于选取的 \(s[l,r]\) 这些字符串,将结束点的子树加一,然后枚举 s[k] 在线段树里面查询答案。

发现这样的话复杂度可以达到优秀的 \(n^2logn\) 。考虑使用主席树,可以降到 \(n^2logn\) (啥也没变啊喂!)

考虑根号分治,对于 \(len_k <= T\) 的部分,使用上面的算法可以做到 \(Tlogn\) 的单次查询。

对于 \(len_k > T\) 的部分,我们首先处理一个数组 \(dp[i][j]\) 表示在 \(s_i\) 中有多少个 \(s_j\) 。预处理出所有 \(s_i > T\)\(dp[i]\)

我们枚举每一个长串,标记串上每一个点,然后对于每一个终止点,在其fail树中的子树里进行查询。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>

#define mid (l+r>>1)
using namespace std;

int read()
{
	int a = 0,x = 1;char ch = getchar();
	while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();}
	return a*x;
}
const int N=1e5+7,T=228;
int n,q,m[N];unsigned s[N/T+7][N];
char S[N];
vector<int>arr[N],g;
int ch[N][26],tot=1,cnt,pos[N],nxt[N];
int add(char *s,vector<int>&a)
{
	int t = 1,len = strlen(s+1);
	for(int i = 1;i <= len;i ++) {
		if(!ch[t][s[i]-'a']) ch[t][s[i]-'a'] = ++tot;
		t = ch[t][s[i]-'a'];a.push_back(t);
	}
	return t;
}
void get_fail()
{
	for(int i = 0;i < 26;i ++) ch[0][i] = 1;
	queue<int>q;q.push(1);
	while(!q.empty()) {
		int u = q.front();q.pop();
		for(int i = 0;i < 26;i ++) {
			if(!ch[u][i]) ch[u][i] = ch[nxt[u]][i];
			else {
				nxt[ch[u][i]] = ch[nxt[u]][i];
				q.push(ch[u][i]);
			}
		}
	}
}

int head[N],go[N],nt[N];
void add(int u,int v)
{
	go[++cnt] = v;
	nt[cnt] = head[u];
	head[u] = cnt;
}

int dfn[N],siz[N];
void dfs(int u)
{
	dfn[u] = ++cnt,siz[u] = 1;
	for(int e = head[u];e;e = nt[e]){
		dfs(go[e]);siz[u] += siz[go[e]];
	}
}

int tre[N<<5],tag[N<<5],rt[N],ls[N<<5],rs[N<<5];
void push_down(int root,int l,int r)
{
	tag[root<<1|1] += tag[root],tag[root<<1] += tag[root];
	tre[root<<1] += (mid-l+1)*tag[root],tre[root<<1|1] += (r-mid)*tag[root];
	tag[root] = 0;
}

void modify(int root,int l,int r,int ql,int qr,int x)
{
	if(l >= ql && r <= qr) {tag[root] += x,tre[root] += (r-l+1)*x;return ;}
	if(l > qr || r < ql) return ;
	push_down(root,l,r);
	modify(root<<1,l,mid,ql,qr,x);modify(root<<1|1,mid+1,r,ql,qr,x);
	tre[root] = tre[root<<1|1] + tre[root<<1];
}
int query(int root,int l,int r,int ql,int qr)
{
	if(l >= ql && r <= qr) return tre[root];
	if(l > qr || r < ql) return 0;
	push_down(root,l,r);
	return query(root<<1,l,mid,ql,qr) + query(root<<1|1,mid+1,r,ql,qr);
}



void modify(int r1,int &r2,int l,int r,int ql,int qr,int x)
{
	if(l > qr || r < ql) return ;
	r2 = ++cnt;ls[r2] = ls[r1],rs[r2] = rs[r1];tre[r2] = tre[r1];tag[r2] = tag[r1];
	if(l >= ql && r <= qr) {tre[r2] += (r-l+1)*x,tag[r2] += x;return ;}
	modify(ls[r1],ls[r2],l,mid,ql,qr,x);modify(rs[r1],rs[r2],mid+1,r,ql,qr,x);
	tre[r2] += x*(min(r,qr)-max(l,ql)+1);
}

int query(int r1,int r2,int l,int r,int ql,int qr)
{
	if(l >= ql && r <= qr) return tre[r2]-tre[r1];
	if(l > qr || r < ql) return 0;
	return (min(qr,r)-max(ql,l)+1)*(tag[r2]-tag[r1]) + query(ls[r1],ls[r2],l,mid,ql,qr) + query(rs[r1],rs[r2],mid+1,r,ql,qr);
}
int id[N],B[N];
void add1(int p,int a,int *B)
{
	for(int i = p;i <= tot;i += i&-i) B[i] += a;
}
int query(int p,int *B)
{
	int ret = 0;
	for(int i = p;i >=1;i -= i&-i) ret += B[i];
	return ret;
}

int main()
{
	// freopen("random.in","r"/,stdin);
	// freopen("sol.out","w",stdout);
	n = read(),q = read();
	for(int i = 1;i <= n;i ++) {
		scanf("%s",S+1);
		pos[i] = add(S,arr[i]);m[i] = strlen(S+1);
		if(m[i] >= T) g.push_back(i),id[i] = g.size();
	}
	get_fail();
	for(int i = 2;i <= tot;i ++) add(nxt[i],i);
	cnt = 0;dfs(1);cnt=0;
	for(int o:g) {
		++cnt;
		for(auto u:arr[o]) {
			add1(dfn[u],1,B);
		}
		for(int i = 1;i <= n;i ++) {
			s[cnt][i] = s[cnt][i-1] + query(dfn[pos[i]]+siz[pos[i]]-1,B) - query(dfn[pos[i]]-1,B);
		}
		for(auto u:arr[o]) {
			add1(dfn[u],-1,B);
		}
	}
	cnt = 0;for(int i = 1;i <= n;i ++) {
		modify(rt[i-1],rt[i],1,tot,dfn[pos[i]],dfn[pos[i]]+siz[pos[i]]-1,1);
	}
	for(int i = 1;i <= q;i ++) {
		int l = read(),r = read(),k = read();
		if(m[k] >= T) {
			printf("%u\n",s[id[k]][r]-s[id[k]][l-1]);
		} else {
			int ret = 0;
			for(int x:arr[k]) {
				int qwq = query(rt[l-1],rt[r],1,tot,dfn[x],dfn[x]);
				ret += qwq;
			}
			printf("%d\n",ret);
		}
	}

	return 0;
}
posted @ 2021-06-29 19:04  nao-nao  阅读(38)  评论(0编辑  收藏  举报