1. 程式人生 > 其它 >CF163E e-Government(AC自動機+樹狀陣列維護fail樹的DFS序)

CF163E e-Government(AC自動機+樹狀陣列維護fail樹的DFS序)

給出n個字串,每個字串有兩種狀態:退役和服役

三種操作:

1)詢問當前服役的字串在詢問字串內的出現次數之和。

2)把一個退役的字串設為服役。

3)把一個服役的字串設為退役。

如何處理操作1?

對所有字串建出AC自動機,建出fail樹,求子樹和。

每個模式串s_i在t中出現的次數之和就是fail樹上根到當前節點的路徑的點權之和。

操作2 3的修改操作,就是把某個點權+1或-1。

所以可以用線段樹維護 每個節點到根節點的路徑點權之和。

單點修改影響的是整個子樹。

這裡涉及到對fail樹的dfs序的區間修改和區間詢問,但是有一個巨巨巨噁心的點,就是卡線段樹,上線段樹一定會MLE。

網上找了個樹狀陣列維護區間修改的板子...

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+500;
int n,q,tr[maxn][26],fail[maxn],tot;
vector<int> g[maxn];
long long cnt[maxn];
string t,s;
int ed[maxn];
int dfn[maxn],id[maxn],tol,sz[maxn];
int insert (string s) {
	int u=0;
	for (char i:s) {
		if (!tr[u][i-'a']) tr[u][i-'a']=++tot;
		u=tr[u][i-'a'];
	}
	cnt[u]++;
	return u;
}
void build () {
	queue<int> q;
	for (int i=0;i<26;i++) {
		if (tr[0][i]) {
			q.push(tr[0][i]);
		}
	}
	while (q.size()) {
		int u=q.front();
		q.pop();
		for (int i=0;i<26;i++) {
			if (tr[u][i]) {
				fail[tr[u][i]]=tr[fail[u]][i];
				q.push(tr[u][i]);
			}
			else {
				tr[u][i]=tr[fail[u]][i];
			}
		}
	}
}
 
 
long long c[maxn<<2],lz[maxn<<2];
void Build (int i,int l,int r) {
	if (l==r) {
		c[i]=cnt[id[l]];
		return;
	}
	int mid=(l+r)>>1;
	Build(i<<1,l,mid);
	Build(i<<1|1,mid+1,r);
	c[i]=c[i<<1]+c[i<<1|1];
}
void pushdown (int i,int l,int r) {
	if (lz[i]) {
		int mid=(l+r)>>1;
		c[i<<1]+=1ll*(mid-l+1)*lz[i];
		lz[i<<1]+=lz[i];
		c[i<<1|1]+=1ll*(r-mid)*lz[i];
		lz[i<<1|1]+=lz[i]; 
		lz[i]=0;
	} 
}
void up (int i,int l,int r,int L,int R,int v) {
	if (l>=L&&r<=R) {
		c[i]+=1ll*(r-l+1)*v;
		lz[i]+=v;
		return;
	}
	pushdown(i,l,r);
	int mid=(l+r)>>1;
	if (L<=mid) up(i<<1,l,mid,L,R,v);
	if (R>mid) up(i<<1|1,mid+1,r,L,R,v);
	c[i]=c[i<<1]+c[i<<1|1];
}
long long query (int i,int l,int r,int L,int R) {
	if (l>=L&&r<=R) return c[i];
	pushdown(i,l,r);
	int mid=(l+r)>>1;
	long long ans=0;
	if (L<=mid) ans+=query(i<<1,l,mid,L,R);
	if (R>mid) ans+=query(i<<1|1,mid+1,r,L,R);
	return ans;
}
 
void dfs (int u,int f) {
	dfn[u]=++tol;
	cnt[u]+=cnt[f];
	id[tol]=u;
	sz[u]=1;
	for (int v:g[u]) {
		if (v==f) continue;
		dfs(v,u);
		sz[u]+=sz[v];
	}
}
int cc[maxn];
int main () {
	ios::sync_with_stdio(false);
	cin>>q>>n;
	if (n==1&&q==1) return printf("1\n"),0;
	for (int i=1;i<=n;i++) {
		cin>>s;
		ed[i]=insert(s);
		cc[ed[i]]++;
	}
	build();
	for (int i=1;i<=tot;i++) {
		g[fail[i]].push_back(i);
		g[i].push_back(fail[i]);
	}
	dfs(0,0);
	Build(1,1,tot+1);
	while (q--) {
		string op;
		cin>>op;
		if (op[0]=='?') {
			long long ans=0;
			int u=0;
			for (int i=1;i<op.size();i++) {
				u=tr[u][op[i]-'a'];
				ans+=query(1,1,tot+1,dfn[u],dfn[u]);
			}
			printf("%lld\n",ans);
		}
		else if (op[0]=='+') {
			int tt=0;
			for (int i=1;i<op.size();i++) {
				tt=tt*10+op[i]-'0';
			}
			if (cc[ed[tt]]) continue; 
			cc[ed[tt]]++;
			up(1,1,tot+1,dfn[ed[tt]],dfn[ed[tt]]+sz[ed[tt]]-1,1);
		}
		else if (op[0]=='-') {
			int tt=0;
			for (int i=1;i<op.size();i++) {
				tt=tt*10+op[i]-'0';
			}
			if (cc[ed[tt]]==0) continue;
			cc[ed[tt]]--;
			up(1,1,tot+1,dfn[ed[tt]],dfn[ed[tt]]+sz[ed[tt]]-1,-1);
		}
	}
}