zoukankan      html  css  js  c++  java
  • 【[NOI2011]阿狸的打字机】

    首先发现这个插入的非常有特点,我们可以直接利用这个特殊的性质在(Trie)树上模拟指针的进退

    之后得到了(Trie)树,先无脑建出(AC)

    之后考虑一下如何写暴力

    最简单的暴力对于每一个询问直接在(AC)机上匹配之后跳(fail),跳到多少次(fail)就代表出现了几次

    显然这并不能通过

    考虑一下优雅的跳(fail)

    发现(fail)指针建出来恰好是一个树的结构,因为一个点的(fail)只能指向唯一的一个点

    把这样一棵(fail)树建出来,我们直接在(fail)树上判断另一个串的结束标记是否在这个点到根的路径上就好了

    可以对(fail)树搞一个(dfs)序,之后把问题转化为单点加,区间查显然可以直接用一个树状数组来维护

    但是这个样子还是要对每一个串都进行一遍这样的操作

    但是考虑到每一个查询操作有很多共用的节点,我们可以直接按照(Trie)上的顺序离线下来,之后利用大量重复的这一特性去统计答案

    代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #define re register
    #define maxn 100005
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define lowbit(x) ((x)&(-x))
    struct E
    {
    	int v,nxt;
    }e[maxn<<1];
    int head[maxn],fa[maxn];
    int n,m,to[maxn],dfn[maxn],DFN[maxn];
    int cnt,num,tot,__,t;
    struct Ask
    {
    	int x,y,rk,ans;
    }a[maxn];
    inline int read()
    {
    	char c=getchar();
    	int x=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();
    	return x;
    }
    inline void add_edge(int x,int y)
    {
    	e[++num].v=y;
    	e[num].nxt=head[x];
    	head[x]=num;
    }
    char S[maxn];
    int c[maxn],sum[maxn];
    inline void add(int x,int val){for(re int i=x;i<=cnt+1;i+=lowbit(i)) c[i]+=val;}
    inline int ask(int x){int now=0;for(re int i=x;i;i-=lowbit(i)) now+=c[i];return now;}
    int son[maxn][26],trie[maxn][26],fail[maxn],Ans[maxn];
    inline void ins()
    {
    	int now=0;
    	fa[now]=0;
    	scanf("%s",S+1);
    	int len=strlen(S+1);
    	for(re int i=1;i<=len;i++)
    	{
    		if(S[i]=='B') 
    		{
    			now=fa[now];
    			continue;
    		}
    		if(S[i]=='P')
    		{
    			to[++tot]=now;
    			continue;
    		}
    		if(!son[now][S[i]-'a']) son[now][S[i]-'a']=trie[now][S[i]-'a']=++cnt,fa[cnt]=now;
    		now=son[now][S[i]-'a'];
    	}
    }
    inline void Build()
    {
    	std::queue<int> q;
    	for(re int i=0;i<26;i++) if(son[0][i]) q.push(son[0][i]);
    	while(!q.empty())
    	{
    		int k=q.front();q.pop();
    		for(re int i=0;i<26;i++)
    		if(son[k][i]) fail[son[k][i]]=son[fail[k]][i],q.push(son[k][i]);
    			else son[k][i]=son[fail[k]][i];
    	}
    }
    inline int cmp(Ask A,Ask B)
    {
    	return dfn[A.x]<dfn[B.x];
    }
    void DFS(int x)
    {
    	dfn[x]=++__;
    	if(x!=fail[x]) add_edge(fail[x],x);//printf("%d
    ",x);
    	for(re int i=0;i<26;i++)
    		if(trie[x][i]) DFS(trie[x][i]);
    }
    void dfs(int x,int F)
    {
    	sum[x]=1;DFN[x]=++__;
    	for(re int i=head[x];i;i=e[i].nxt)
    	if(e[i].v!=F)
    	{
    		dfs(e[i].v,x);
    		sum[x]+=sum[e[i].v];
    	}
    }
    void Dfs(int x)
    {
    	__++;
    	add(DFN[x],1);
    	while(a[t].x==x)
    	{
    		int Y=to[a[t].y];
    		a[t].ans=ask(DFN[Y]+sum[Y]-1)-ask(DFN[Y]-1);
    		t++;
    	}
    	for(re int i=0;i<26;i++)
    		if(trie[x][i]) Dfs(trie[x][i]);
    	add(DFN[x],-1);
    }
    int main()
    {
    	ins();
    	Build(),DFS(0);
    	n=read();
    	for(re int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),a[i].rk=i,std::swap(a[i].x,a[i].y),a[i].x=to[a[i].x];
    	std::sort(a+1,a+n+1,cmp);
    	__=0,dfs(0,0),__=0;t=1;
    	Dfs(0);
    	for(re int i=1;i<=n;i++) Ans[a[i].rk]=a[i].ans;
    	for(re int i=1;i<=n;i++) printf("%d
    ",Ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Java 线程具有五中基本状态 ?
    可以直接调用 Thread 类的 run ()方法么?
    创建线程的三种方式的对比?
    FutureTask 是什么 ?
    什么是 CAS?
    如何让正在运行的线程暂停一段时间?
    什么是不可变对象(immutable object)?Java 中怎么 创建一个不可变对象?
    创建线程的有哪些方式?
    Semaphore 有什么作用 ?
    CopyOnWriteArrayList 可以用于什么应用场景?
  • 原文地址:https://www.cnblogs.com/asuldb/p/10205663.html
Copyright © 2011-2022 走看看