• 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
    "),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
    ",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);
    		}
    	}
    }
    
    
  • 相关阅读:
    自我介绍
    最大连通子数组求和
    敏捷开发方法综述
    第四周学习进度条
    时间日志和缺陷日志
    最大子数组2.0
    最大子数组1.0
    第三周学习进度条
    小学四则运算3.0
    单元测试
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15006774.html
Copyright © 2020-2023  润新知