cf 163e e-Government

此题是那次做了GRE的题目后,相同做法的题。

先接着上次说的说说fail树吧:考虑把fail的指针反向建边,由于对于fail节点每个节点所指向的fail唯一,根据fail的原理,我们会从某个节点返回到root,所以fail的反向建边后性质就成了个fail树,这个树表示的是对于如果访问到某个子节点,那么他的祖先节点必然可以作为子串出现,那么鉴于此,我们要统计子串访问多少次,就是该节点返回到root的次数,那么这就是我前面说的dfs序。

题意是:给出n个名字,3种操作,

?string 统计前面是城市居民的作为子串出现在string的次数

+idx 把第idx个人变为城市居民

- idx 把第idx个人变为非城市居民

对于每个?操作,输出次数

现在我们还是那题那样对于每个的总结点标号为idx,然后由于询问有把第idx排除出去的,所有我们要有个映射对应于哪些trie上的节点访问过,可以用bool vis来标记

然后对于dfs序列,我们可以将每个访问过的节点,它所能表示的一段子串+1(即dfs序连续的一段)

对于每个询问,我们只要对应于前面的idx标号,把每个从root一直下去经过的点查询出来就可以了,把其对应的点看有多少点访问过就可以了

            if(str[0]=='?')
            {
                int u=0,ans=0;
                for(int i=1;i<len;i++)
                {
                    int c=ac.idx(str[i]);
                    u=ac.ch[u][c];
                    ans+=tree.query(1,L[u]);
                }
                cout<<ans<<endl;
            }


对于+-操作其实是可以看做一个同等操作,

我们将前面映射的点所对应的区间L,R全部+1或者-1就代表一个删除操作了 

            if(str[0]=='+')
            {
                int num=0;
                for(int i=1;i<len;i++)
                {
                    num=num*10+str[i]-'0';
                }
                if(flag[num])continue;
                flag[num]=1;
                tree.update(1,L[ac.mp[num]],R[ac.mp[num]],1);
            }
-1操作是一样的

然后这里对于dfs序列一段区间的更新操作用线段树就可以了 或者bit也可以

线段树维护的就是区间和,和那个GRE的题目一样

其中ac自动机是模板,dfs序列也很简单对于fail指针反向建边就可以了

for(int i=1;i<=ac.sz;i++)
    {addedge(ac.fail[i],i);addedge(i,ac.fail[i]);}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值