zoukankan      html  css  js  c++  java
  • 安徽师大附中%你赛day2T3 巧克力 解题报告

    巧克力

    题目描述

    (T)(N)块巧克力, 每块巧克力上都有一句话(由小写英文字母组成,不含标点) 。现在每块巧克力都断成了若干截,更糟糕的是,有一些碎片丢失了 ,但是剩下的碎片之间的顺序是可以辨识的。
    形式化地,我们用 一个只含小写字母和#的字符串来代表一块巧克力, 其中#表示该位置断开了,可能缺失了一段字符, 也可能没有。
    例如: 如果我们用(a)#(a)来表示一块巧克力, 则原来它上面的话可能是(aa), 也可能是(aaa)(aaczraa)等等, 但不会是(aab)
    (T)(N)块巧克力, 每块巧克力上都有一句话(由小写英文字母组成,不含标点) 。现在每块巧克力都断成了若干截,更糟糕的是,有一些碎片丢失了 ,但是剩下的碎片之间的顺序是可以辨识的。
    形式化地,我们用 一个只含小写字母和#的字符串来代表一块巧克力, 其中#表示该位置断开了,可能缺失了一段字符, 也可能没有。
    例如: 如果我们用(a)#(a)来表示一块巧克力, 则原来它上面的话可能是(aa), 也可能是(aaa)(aaczraa)等等, 但不会是(aab)

    输入输出格式

    输入数据

    输入第一行, 一个正整数 (N)
    接下来(N)行, 每行一个字符串 , 意义如题所述一个非负整数, 表示你给出的答案

    输出格式:

    一个非负整数, 表示你给出的答案

    对于所有的数据:
    (2≤N≤500, 000), 所有字符串 的总长不超过 (1, 000, 000)

    对于每个子任务的特殊限制:

    Subtask1(12pts) : 满足性质①
    所有字符串 长度相等

    Subtask2(20pts) : 满足性质①

    Subtask3(23pts) : (N≤1, 000)

    Subtask4(45pts) : 无特殊限制

    性质①: 每个字符串包含恰好一个#,且这个#在字符串的开头。


    感觉这个题出的非常好,考场上只拿了前两个Subtask,刚第二题去了(结果第二题还凉了)

    其实第两个测试点就可以给出后面的启示了。

    我们前两个测试点的字符倒着搞进字典树里,然后随便dfs一下统计就行了

    观察手玩我们发现,如果两个字符串的前缀和后缀均有包含关系(这里的指一直到第一个“#”位置),那么它们两个可以搞

    证明可以从递归构造的角度证明

    我们把前缀和后缀分别搞进两棵字典树,就可以随便搞出(N^2)的算法了
    (满足四个点两两是祖孙关系)

    如何进一步优化复杂度呢?

    我们考虑像第二步那样,(dfs)一棵树,然后随时统计另一颗树的贡献。

    考虑我们现在做到第一棵树的点(i),这时候我们已经激活了(i)到根的在第二棵树上可能产生贡献的点了。

    发现(i)在第二棵树上的点的子树已及它到根的那条链的点权产生了贡献

    用两个树状数组维护这个过程,一个维护子树贡献。

    另一个讨论链上的点对它的子树都有贡献,实际上是子树加,单点查,差分维护


    Code:

    #include <cstdio>
    #define ls now<<1
    #define rs now<<1|1
    #include <cstring>
    #include <vector>
    #define ll long long
    using namespace std;
    const int N=1e6+10;
    int ch[N<<1][26],tot=1,root[2]={0,1},n;
    char c[N];
    vector <int > to[N<<1];
    ll ans,cnt[N<<1];
    void in()
    {
        scanf("%s",c);
        int len=strlen(c),now=root[0];
        for(int i=0;i<len&&c[i]!='#';i++)
        {
            if(!ch[now][c[i]-'a']) ch[now][c[i]-'a']=++tot;
            now=ch[now][c[i]-'a'];
        }
        ++cnt[now];
        int las=now;
        now=root[1];
        for(int i=len-1;i&&c[i]!='#';i--)
        {
            if(!ch[now][c[i]-'a']) ch[now][c[i]-'a']=++tot;
            now=ch[now][c[i]-'a'];
        }
        ++cnt[now];
        to[las].push_back(now);
    }
    int siz[N<<1],dfn[N<<1];
    int dfs_order(int now)
    {
        dfn[now]=++n;
        siz[now]=1;
        for(int i=0;i<26;i++)
            if(ch[now][i])
                siz[now]+=dfs_order(ch[now][i]);
        return siz[now];
    }
    ll sum[2][N];
    ll query(int typ,int pos)
    {
        ll x=0;
        while(pos)
        {
            x+=sum[typ][pos];
            pos-=pos&-pos;
        }
        return x;
    }
    void change(int typ,int pos,ll del)
    {
        while(pos<=n)
        {
            sum[typ][pos]+=del;
            pos+=pos&-pos;
        }
    }
    void dfs(int now)
    {
        for(int i=0;i<to[now].size();i++)
        {
            int num=to[now][i];
            ans+=query(0,dfn[num]+siz[num]-1)-query(0,dfn[num]-1);
            ans+=query(1,dfn[num]);
            change(0,dfn[num],1);
            change(1,dfn[num]+1,1);
            change(1,dfn[num]+siz[num],-1);
        }
        for(int i=0;i<26;i++)
            if(ch[now][i])
                dfs(ch[now][i]);
        for(int i=0;i<to[now].size();i++)
        {
            int num=to[now][i];
            change(0,dfn[num],-1);
            change(1,dfn[num]+1,-1);
            change(1,dfn[num]+siz[num],1);
        }
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) in();
        n=0;
        siz[root[1]]=dfs_order(root[1]);
        dfs(root[0]);
        printf("%lld
    ",ans);
        return 0;
    }
    
    

    2018.8.14

  • 相关阅读:
    结构本身和结构成员在内存中储存的形式
    C语言字符,字符串,字节操作常用函数
    可变参数列表
    用数组代替指针实现静态链表
    cout对象一些常用方法的总结
    cin对象的一些常用方法使用总结
    数据结构基本概念和术语总结
    GCH文件
    Ubuntu16 搭建Git 服务器
    Navicat 连接VMware中Ubuntu 下的mysql5.7遇到的坑
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9477032.html
Copyright © 2011-2022 走看看