• 【BZOJ3926】【ZJOI2015】诸神眷顾的幻想乡


    题意:

    Description

    幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日。 

    粉丝们非常热情,自发组织表演了一系列节目给幽香看。幽香当然也非常高兴啦。 
    这时幽香发现了一件非常有趣的事情,太阳花田有n块空地。在过去,幽香为了方便,在这n块空地之间修建了n-1条边将它们连通起来。也就是说,这n块空地形成了一个树的结构。 
    有n个粉丝们来到了太阳花田上。为了表达对幽香生日的祝贺,他们选择了c中颜色的衣服,每种颜色恰好可以用一个0到c-1之间的整数来表示。并且每个人都站在一个空地上,每个空地上也只有一个人。这样整个太阳花田就花花绿绿了。幽香看到了,感觉也非常开心。 
    粉丝们策划的一个节目是这样的,选中两个粉丝A和B(A和B可以相同),然后A所在的空地到B所在的空地的路径上的粉丝依次跳起来(包括端点),幽香就能看到一个长度为A到B之间路径上的所有粉丝的数目(包括A和B)的颜色序列。一开始大家打算让人一两个粉丝(注意:A,B和B,A是不同的,他们形成的序列刚好相反,比如红绿蓝和蓝绿红)都来一次,但是有人指出这样可能会出现一些一模一样的颜色序列,会导致审美疲劳。 
    于是他们想要问题,在这个树上,一共有多少可能的不同的颜色序列(子串)幽香可以看到呢? 
    太阳花田的结构比较特殊,只与一个空地相邻的空地数量不超过20个。 

    Input

     第一行两个正整数n,c。表示空地数量和颜色数量。 

    第二行有n个0到c-1之间,由空格隔开的整数,依次表示第i块空地上的粉丝的衣服颜色。(这里我们按照节点标号从小到大的顺序依次给出每块空地上粉丝的衣服颜色)。 
    接下来n-1行,每行两个正整数u,v,表示有一条连接空地u和空地v的边。 

    Output

    一行,输出一个整数,表示答案。

    HINT

    对于所有数据,1<=n<=100000, 1<=c<=10。

    对于15%的数据,n<=2000。

    另有5%的数据,所有空地都至多与两个空地相邻。

    另有5%的数据,除一块空地与三个空地相邻外,其他空地都分别至多与两个空地相邻。

    另有5%的数据,除某两块空地与三个空地相邻外,其他空地都分别至多与两个空地相邻。

    题解:

    Orz clj 陈老师神题(可能是神(du)仙(liu)ZJOI的开端?)

    首先要注意到数据的一个关键性质:叶子节点不会超过20个,所以肯定要将题目关于叶子节点做一些转化;

    题目中的树显然可以看成一棵trie,把$A$到$B$路径上的点连起来就能得到一个字符串;

    考虑一个在trie树上“转弯”的路径,即从$u$经过$lca(u,v)$再到$v$的一条路径,假如把整棵树以$u$或者$v$的某棵子树中的叶子节点作为根重构的话,就会变成一条深度严格递增的“直”路径,当然原本在trie树上就是“直”的路径也一样;

    综合起来就是以每个叶子节点为根重构整棵trie,就一定能把原来trie上的每条路径变成一条深度严格递增的路径;

    显然这样的路径必定是此时根节点到某个节点路径上字符串的一个后缀;

    那么直接对于所有重构出的trie建出广义后缀自动机,要求所有不相同的子串数量,就利用后缀自动机基本常识统计答案$max[s]-max[fa[s]]$即可;

    这里实现广义后缀自动机就在trie上dfs,然后每次把last设为父节点在广义后缀自动机上对应的节点即可(这样做复杂度不是严格线性的,具体见lyy2015国家集训队论文,但是出题人没卡……)

    代码:

    没能1A差评

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #include<cmath>
     6 #include<queue>
     7 #define inf 2147483647
     8 #define eps 1e-9
     9 using namespace std;
    10 typedef long long ll;
    11 typedef double db;
    12 struct edge{
    13     int v,next;
    14 }a[200001];
    15 int n,m,u,v,tote=0,rt=1,tot=1,last=1,d[100001],c[100001],head[100001],son[2000001][11],fa[2000001],mx[2000001];
    16 ll ans=0;
    17 void add(int u,int v){
    18     a[++tote].v=v;
    19     a[tote].next=head[u];
    20     head[u]=tote;
    21 }
    22 int extend(int p,int ch){
    23     int np=++tot;
    24     mx[np]=mx[p]+1;
    25     for(;p&&!son[p][ch];p=fa[p])son[p][ch]=np;
    26     if(!p)fa[np]=rt;
    27     else{
    28         int q=son[p][ch];
    29         if(mx[q]==mx[p]+1)fa[np]=q;
    30         else{
    31             int nq=++tot;
    32             mx[nq]=mx[p]+1;
    33             memcpy(son[nq],son[q],sizeof(son[q]));
    34             fa[nq]=fa[q];
    35             fa[q]=fa[np]=nq;
    36             for(;p&&son[p][ch]==q;p=fa[p])son[p][ch]=nq;
    37         }
    38     }
    39     return last=np;
    40 }
    41 void dfs(int u,int fa,int p){
    42     int nw=extend(p,c[u]);
    43     for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
    44         int v=a[tmp].v;
    45         if(v!=fa){
    46             dfs(v,u,nw);
    47         }
    48     }
    49 }
    50 int main(){
    51     memset(head,-1,sizeof(head));
    52     memset(d,0,sizeof(d));
    53     scanf("%d%d",&n,&m);
    54     for(int i=1;i<=n;i++){
    55         scanf("%d",&c[i]);
    56     }
    57     for(int i=1;i<n;i++){
    58         scanf("%d%d",&u,&v);
    59         add(u,v);
    60         add(v,u);
    61         d[u]++,d[v]++;
    62     }
    63     for(int i=1;i<=n;i++){
    64         if(d[i]==1){
    65             dfs(i,-1,rt);
    66         }
    67     }
    68     for(int i=2;i<=tot;i++){
    69         ans+=(mx[i]-mx[fa[i]]);
    70     }
    71     printf("%lld",ans);
    72     return 0;
    73 }
  • 相关阅读:
    递归斐波那契数列时间复杂度
    动态规划 矩阵链乘法
    kmp算法
    贪心 单源最短路径
    贪心法 背包问题求解
    贪心法 货币支付问题或找零问题
    贪心算法简介
    排列问题的递归算法和非递归算法
    php __set() __get() __isset() __unset()四个方法的应用
    使用栈结构完毕四则运算
  • 原文地址:https://www.cnblogs.com/dcdcbigbig/p/10130635.html
Copyright © 2020-2023  润新知