码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj3670 [Noi2014]动物园

时间:2020-02-28 22:53:37      阅读:50      评论:0      收藏:0      [点我收藏+]

标签:节点   一半   scan   strlen   algorithm   个数   fine   http   turn   

zz:https://www.cnblogs.com/GXZlegend/p/6855581.html

Sol:

先求出next,然后我们可以发现如果x和next[x](x>0)连一条边,那么就是一颗树,而所求的num是每个点的所有祖先节点中最后一个长度小于等于len[x]的点之前的祖先节点个数-1(0不为答案),也即祖先节点中最后一个长度小于等于len[x]的点的深度(deep[0]=0)。于是我们可以维护一个栈,并在其中二分查找得到答案。

#include <bits/stdc++.h>
#include <cstring>
#include <algorithm>
#define N 1000010
using namespace std;
int fail[N] , num[N] , head[N] , to[N] , next[N] , cnt , sta[N] , top;
char str[N];
void add(int x , int y)
{
    to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
    int i;
    sta[++top] = x; //将编号为x的号进栈 
    for(i = head[x] ; i ; i = next[i])
    {
	
     num[to[i]] = upper_bound(sta + 1 , sta + top + 1 , to[i] >> 1) - sta - 1 ;
     //求出to[i]这个点,所在的栈中,有多少值是小于它的一半的。 
     //upper_bound是找第一个大于某个元素的位置,也就是找有多少个数是小于它的. 
	 dfs(to[i]);
    }  
    sta[top -- ] = x;
}
int main()
{
    int T;
    scanf("%d" , &T);
    while(T -- )
    {
        int n , i = 0 , j = -1;
        long long ans = 1;
        memset(head , 0 , sizeof(head)) , cnt = 1;
        scanf("%s" , str) , n = strlen(str);
        fail[0] = -1;
        while(i < n)
        {
            while(~j && str[j] != str[i]) j = fail[j];
            fail[++i] = ++j;
        }
       // for (int i=1;i<=n;i++)
         //  cout<<i<<"    "<<fail[i]<<endl;
        for(i = 1 ; i <= n ; i ++ ) //连一条边从fail[i]到i 
		    add(fail[i] , i);
        dfs(0);//从0号点开始,将其入栈,这样每个num[i]的值于少为1 
        for(i = 1 ; i <= n ; i ++ ) 
		     ans = ans * num[i] % 1000000007;
        printf("%lld\n" , ans);
    }
    return 0;
}

  

bzoj3670 [Noi2014]动物园

标签:节点   一半   scan   strlen   algorithm   个数   fine   http   turn   

原文地址:https://www.cnblogs.com/cutemush/p/12380457.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!