zoukankan      html  css  js  c++  java
  • CF1207G Indie Album(AC自动机+线段树维护DFS序)

    有n次操作,格式为1 c或2 j c,分别表示新建一个为c的字符串,和在第j次操作得到的串后接上c后新建一个字符串。

    接着是m次询问,每次询问版本i的串中,t的出现次数。

    m次询问,每次询问版本i的字符串中,t的出现次数。

    如果暴力建字符串肯定存不下。

    考虑a aa aaa aaaa aaaaa...这种情况,必爆。

    现在考虑优化操作2。

    称每种操作2所修改的字符串编号为它的父亲。

    每个由操作2得到的字符串的父亲节点唯一。

    那么可以先对所有字符串建一颗字符树。

    然后把询问离线,即每种字符串i对应的询问集中处理。

    然后,先对所有t串建AC自动机。

    然后在字符树上搜索,维护当前节点到根节点的路径。

    为了方便操作,加一个巨大的根节点,编号为0。

    往下搜,就把对应下一个节点的AC自动机上的节点编号塞入路径。

    回溯的时候,把路径的最后一个节点编号退出即可。

    然后,对当前路径,依次处理询问。

    加入路径的时候,在fail树上让对应点权+1。

    处理单个询问的t串的时候,询问子树和就可以。

    这样就可以用线段树维护fail树的dfs序来处理子树和。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=4e5+100;
    vector<int> gg[maxn];
    char a[maxn];
    string t[maxn];
    vector<int> qq[maxn];
    int n,m,q;
    int tr[maxn][26],fail[maxn],sz[maxn],tot;
    vector<int> g[maxn];
    int ed[maxn];
    int dfn[maxn],cnt;
    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'];
    	}
    	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];
    			}
    		}
    	}
    }
     
    int c[maxn<<2];
    void up (int i,int l,int r,int p,int v) {
    	if (l==p&&r==p) {
    		c[i]+=v;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if (p<=mid) up(i<<1,l,mid,p,v);
    	if (p>mid) up(i<<1|1,mid+1,r,p,v);
    	c[i]=c[i<<1]+c[i<<1|1];
    }
    int query (int i,int l,int r,int L,int R) {
    	if (l>=L&&r<=R) return c[i];
    	int mid=(l+r)>>1;
    	int 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 dfs1 (int u) {
    	sz[u]=1;
    	dfn[u]=++cnt;
    	for (int v:g[u]) {
    		dfs1(v);
    		sz[u]+=sz[v]; 
    	}
    }
    int ans[maxn];
    void dfs2 (int u,int uu) {
    	for (int v:qq[u]) ans[v]=query(1,1,cnt,dfn[ed[v]],dfn[ed[v]]+sz[ed[v]]-1);
    	for (int v:gg[u]) {
    		up(1,1,cnt,dfn[tr[uu][a[v]-'a']],1);
    		dfs2(v,tr[uu][a[v]-'a']);
    		up(1,1,cnt,dfn[tr[uu][a[v]-'a']],-1);
    	}	
    }
    int main () {
    	ios::sync_with_stdio(false);
    	cin>>n;
    	while (n--) {
    		int op;
    		cin>>op;
    		if (op==1) {
    			m++;
    			gg[0].push_back(m);
    			char ch;
    			cin>>ch;
    			a[m]=ch;
    		}
    		else {
    			int x;
    			char ch;
    			cin>>x>>ch;
    			m++;
    			gg[x].push_back(m);
    			a[m]=ch;
    		}
    	}
    	cin>>q;
    	for (int i=1;i<=q;i++) {
    		int x;
    		cin>>x>>t[i];
    		qq[x].push_back(i);
    		ed[i]=insert(t[i]);
    	}
    	build();
    	for (int i=1;i<=tot;i++) g[fail[i]].push_back(i);
    	dfs1(0);
    	dfs2(0,0);
    	for (int i=1;i<=q;i++) printf("%d
    ",ans[i]);
    	
    }
    
    
  • 相关阅读:
    P1642 规划 [01分数规划]
    01分数规划学习笔记
    P1527 [国家集训队]矩阵乘法 [整体二分]
    P3292 [SCOI2016]幸运数字 [线性基+倍增]
    java中遍历集合的三种方式
    20190706中兴提前批专业面面经
    《java入门如此简单》——语句,函数和数组
    java中数组常见的操作
    2019 波克城市ava面试笔试题 (含面试题解析)
    2019 华云数据java面试笔试题 (含面试题解析)
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/15008529.html
Copyright © 2011-2022 走看看