首页 > 其他分享 >CODE FESTIVAL 2016 qual B E 题解

CODE FESTIVAL 2016 qual B E 题解

时间:2023-05-30 20:46:03浏览次数:38  
标签:include string FESTIVAL 题解 int trie 400010 2016 Sigma

以下 \(\Sigma\) 为字符集。

首先单次询问 \(O(|\Sigma||S|)\) 的暴力是显然的:建出 trie 树,然后每次把对应的字符串在上边扫,加上对应位置比它小的子树的大小。

然后接下来有两种方法。

正解

首先在线大概是没什么前途的,考虑离线,建出 trie 树之后在上边 dfs,处理挂在每个节点上的询问。具体的,我们记录每个字符串在 trie 上的结尾位置,把询问挂在上边。

考虑 \(s\) 字典序比 \(t\) 更小的条件:要么 \(s\) 是 \(t\) 的前缀,要么 \(s\) 和 \(t\) 在第一位出现不同的位置 \(i\) 满足 \(s_i<t_i\)。第一种可以在每个串的结尾打标记,dfs 的时候扫到一个点就加上。考虑第二种。

题解给出了这样一种做法:对 \(|\Sigma|^2\) 种不同的字符大小关系分别计算贡献,然后加起来。我们可以记录一个 \(val_{i,j}\) 表示字符 \(i\) 比字符 \(j\) 大时的贡献,那么对于每个询问我们可以 \(O(|\Sigma|^2)\) 地扫每一对字符累加 \(val\)。\(val\) 的更新是简单的,dfs 时搜一个儿子 \(i\),那么对于所有兄弟 \(j\),把当前的 \(val_{i,j}\) 加上 \(j\) 子树的大小。

总复杂度 \(O((n+q)|\Sigma|^2)\)。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
int n,m,num,trie[400010][26],sz[400010],cnt[400010],id[100010];
string s[100010];
void ins(string s,int x){
    int p=0;
    for(int i=0;i<s.length();i++){
        if(!trie[p][s[i]-'a'])trie[p][s[i]-'a']=++num;
        p=trie[p][s[i]-'a'];sz[p]++;
    }
    cnt[p]++;id[x]=p;
}
int sum,ans[100010],tmp[26],val[26][26];
vector<pair<string,int> >q[400010];
void dfs(int x){
    sum+=cnt[x];
    for(pair<string,int>p:q[x]){
        for(int i=0;i<26;i++)tmp[p.first[i]-'a']=i;
        ans[p.second]=sum;
        for(int i=0;i<26;i++){
            for(int j=i+1;j<26;j++){
                if(tmp[i]<tmp[j])ans[p.second]+=val[j][i];
                else ans[p.second]+=val[i][j];
            }
        }
    }
    for(int i=0;i<26;i++){
        if(!trie[x][i])continue;
        for(int j=0;j<26;j++){
            if(i==j)continue;
            if(trie[x][j])val[i][j]+=sz[trie[x][j]];
        }
        dfs(trie[x][i]);
        for(int j=0;j<26;j++){
            if(i==j)continue;
            if(trie[x][j])val[i][j]-=sz[trie[x][j]];
        }
    }
    sum-=cnt[x];
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>s[i];ins(s[i],i);
    }
    cin>>m;
    for(int i=1;i<=m;i++){
        int od;cin>>od>>s[0];
        q[id[od]].push_back(make_pair(s[0],i));
    }
    dfs(0);
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}

暴力

感觉这个暴力比正解更具有启发性(

我们仍然建出 trie 树。然后仿照后缀树,我们把 trie 压缩一下。我们把不是一个串的结尾且只有一个儿子的节点去掉,那么剩下的点要么是一个串的终点,要么不止一个儿子。容易证明这时 trie 的树高是 \(O(\sqrt{|S|})\) 的,因为最多有 \(O(\sqrt{|S|})\) 种不同的长度。于是我们在压缩 trie 上跑暴力就能过了。

复杂度 \(O(|\Sigma|(|S|+q\sqrt{|S|}))\)。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
int n,m,num,trie[400010][26],sz[400010],len[400010],cnt[400010];
string s[100010];
void ins(string s,int x){
    int p=0;
    for(int i=0;i<s.length();i++){
        if(!trie[p][s[i]-'a'])trie[p][s[i]-'a']=++num;
        p=trie[p][s[i]-'a'];sz[p]++;
    }
    cnt[p]++;
}
void dfs(int x){
    int son=0,tmp=0;len[x]=1;
    for(int i=0;i<26;i++){
        if(trie[x][i]){
            tmp++,son=trie[x][i];dfs(trie[x][i]);
        }
    }
    if(tmp==1&&!cnt[x]){
        for(int i=0;i<26;i++)trie[x][i]=trie[son][i];
        len[x]=len[son]+1;cnt[x]+=cnt[son];
    }
}
int query(string s,string tmp){
    int p=0,ans=cnt[0];
    for(int i=len[0]-1;i<s.length();i+=len[p],ans+=cnt[p]){
        for(int j=0;j<26;j++){
            if(tmp[j]==s[i])break;
            if(trie[p][tmp[j]-'a'])ans+=sz[trie[p][tmp[j]-'a']];
        }
        p=trie[p][s[i]-'a'];
    }
    return ans;
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>s[i];ins(s[i],i);
    }
    dfs(0);
    cin>>m;
    while(m--){
        int id;string tmp;cin>>id>>tmp;
        cout<<query(s[id],tmp)<<'\n';
    }
    return 0;
}

标签:include,string,FESTIVAL,题解,int,trie,400010,2016,Sigma
From: https://www.cnblogs.com/gtm1514/p/17444345.html

相关文章

  • CF1398E Two Types of Spells 题解 set
    题目链接:https://codeforces.com/problemset/problem/1398/E题目大意你有一个集合,初始为空。有两种类型的元素,一种是普通元素,一种是强化元素,两种元素都有一个数值。有\(n\)次操作,每次操作会往集合中加入一个元素或者删除一个元素。每次操作后,你都需要确定集合中元素的一个......
  • 第十四届蓝桥杯大赛青少组全国总决赛初级组C++C++题解
    第十四届蓝桥杯大赛青少组全国总决赛初级组\(C++\)题解第一题给定一个十进制正整数\(N(1≤N≤10^9)\),请从小到大输出\(1\)~\(N\)之间(含\(1\)和\(N\))所有满足以下要求的数:这个数转换为八进制后是一个回文数;这个数是一个平方数。例如:\(N=20\),在\(1\)~\(20\)之间满足要求......
  • 山东二轮省集题解合集
    山东二轮省集题解合集Day1A打表,发现答案是\(\prod\limits_{i=1}^n(2i-1)\)。证明可以考虑拿GF推。首先有dp,\(f(i,j)\)表示到第\(i\)个括号当前左括号减右括号的个数为\(j\),转移是简单的\(f(i,j)=f(i,j+1)+f(i,j-1)\times(j-1)\)把\(f(i,j)\)写成一个形式幂级......
  • 欢乐结训赛题解
    欢乐结训赛题解A题目链接题目大意给你一个字符串,让你求字符串中最大的字母在字母表中排第几例如codeforces中s的是最大的s在字母表中排19位代码#include<bits/stdc++.h>usingnamespacestd;constintN=2e5+10;constintmod=1e9+7;voidsolve()......
  • 第十二届蓝桥杯c++b组国赛题解(还在持续更新中...)
    试题A:带宽解题思路:由于小蓝家的网络带宽是200Mbps,即200Mb/s,所以一秒钟可以下载200Mb的内容,根据1B=8b的换算规则,所以200Mb=200/8MB=25MB。所以小蓝家的网络理论上每秒钟最多可以从网上下载25MB的内容。代码实现:#include<iostream>#include<algorithm>usingnamespacestd......
  • yarn安装报错网络问题解决方案
    yarn安装报错网络问题解决方案报错为infoThereappearstobetroublewithyournetworkconnection.Retrying...解决方案:更换安装依赖的镜像,使用淘宝镜像安装安装好后更换淘宝镜像yarnconfigsetregistryhttps://registry.npm.taobao.org移除原代理yarn......
  • Java中Collection与Collections有什么区别?Java常见面试题解析
    本文将为大家详细讲解Java中Collection与Collections的区别点,这是我们进行开发时经常用到的知识点,也是大家在学习Java中很重要的一个知识点,更是我们在面试时有可能会问到的问题!文章较长,干货满满,建议大家收藏慢慢学习。文末有本文重点总结,主页有全系列文章分享。技术类问题,欢迎大......
  • [PKUCPC2023] J. Hat Puzzle 题解
    题目链接:http://poj.openjudge.cn/campus2023/J/很荣幸参与了命题。题解的ppt版本在这儿:https://disk.pku.edu.cn:443/link/E4B484E7F3C58A45E9E4FB19C731BF4E.贴一下md版题解,要比ppt版本的简略一些:每个人能够推断出自己帽子颜色的信息,仅有两类:前面的人的帽子情况,以及其......
  • AtCoder Beginner Contest 303 题解 A - E
    A-SimilarString题目大意忽略0和o的差别以及1和l的差别比较两个字符串。解题思路可以硬求,直接写个超长的if判断一下。可以对两个字符串进行修改,0都换成o,1都换成l,然后直接比较两个字符串。ACCode硬求#include<iostream>#include<algorithm>#include<cstring>#i......
  • P9356 「SiR-1」Bracket 题解
    P9356「SiR-1」Bracket题解首先我们来先考虑一下如何计算一个给定的\(f(s[1,n])\)。一般括号序列的题目都是比较套路的将\(\texttt{(}\)赋值为\(1\),将\(\texttt{)}\)赋值为\(-1\),然后求一下前缀和记为\(sum_i\),那么一个括号序列是合法的当且仅当\(\foralli\in[1,n],......