21.6.21 test
\(NOI\) 模拟赛
字符串滚出 \(OI\)
看到题目名称,\(T1\) 串,\(T2\) 两个串,\(T3\) K个串,我 \(\cdots\),血压已经上来了。
\(T1\) 写了 \(O(n^2)\) 的暴力15pts,正解是AC自动机上面做dp???我还是太菜了,dp没看懂。
\(T2\) 总觉得是在kmp上面修改,但被自己hack了,又加了个小数据跑暴力的部分(貌似数据没有在小数据上卡一般的kmp)40pts。正解是拿出了一个神奇的柿子加上FFT???
\(T3\) \(O(n^2)\) 暴力写挂了,正解是主席树加堆 ,有想过主席树但是被自己否定了。
update on 21.6.22
发现是BZOJ原题,就不开隐藏了,当题解写一下,放一下代码。
T1:串
这篇Blog写的比老师给的易懂(逃。所以写的是这篇里的思想和我的理解。对字符串不是很会,所以很多地方要感性理解一下。
题意:
给出一个字符串集,求有多少字符串可拆分为非空的两部分,每部分都是集合中某一个字符串的前缀。
题解:
考虑枚举所有不重复的前缀拼接,总前缀数为 \(m\),如果把重复的都计算,那么答案为 \(m^2\),于是考虑算出重复的字符串数。
如果一个字符串有重复,那么它一定可以有多种分解方式。如:
有 \((head,a)+(a,end)\) 和 \((haed,b)+(b,end)\) 两种分解。拼接的是前缀,所以我们可以判断 \((head,a),(a,end),(haed,b),(b,end),(a,b)\) 都是出现在字符串集中的前缀,其中 \((a,b)\) 既是 \((head,b)\) 的后缀,又是 \((a,end)\) 的前缀。由于 AC 自动机上的 fail 数组正是 AC 自动机上存在的最长后缀,方便处理题中的后缀,所以把所有的字符串都丢到 AC 自动机上面。考虑枚举 \((a,end)\),那么它的 fail 就是它的最长后缀,此时即为最近的 b。可以预处理出以 \((a,b)\) 为后缀的字符串数,这就是以 \((a,b)\) 为中间串,\((b,end)\) 为后面串的贡献。枚举所有分割点 \(a,b,c,d,e,\cdots\) 都这样操作,除了最后一个 fail=0 的保留,刚好可以把所有重复的字符串去除。因为只要这个分割点 fail 不等于 0,就说明这种分割方式一定可以被后面 fail 的方式替代。
在实际操作中,我们只需要枚举 \((a,end)\) (也就是所有 fail 不为 0 的节点),它的 fail 为 \((b,end)\) ,那么沿着 a 的 fa 向上找 len[\((b,end)\)] 次后就表示\((a,b)\),减去以它为后缀的前缀数量。用字典树大小的平方减去所有贡献后即为最终答案。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
string c[10005];
int ch[300005][30],cn=0;
int fail[300005],cnt[300005],dis[300005],fa[300005];//cnt即以它为后缀的前缀数量
void insert(string s){
int len=s.length(),p=0;
for(int i=0;i<len;i++){
int t=s[i]-'a'+1;
if(!ch[p][t])
ch[p][t]=++cn;
dis[ch[p][t]]=dis[p]+1;
fa[ch[p][t]]=p;
p=ch[p][t];
}
}
void getfail(){
queue<int> q;
int p=0;
for(int i=1;i<=26;i++){
if(ch[p][i]){
q.push(ch[p][i]);
fail[ch[p][i]]=0;
}
}
while(!q.empty()){
p=q.front();
q.pop();
for(int i=1;i<=26;i++){
if(!ch[p][i])continue;
int v=ch[p][i],j;
for(j=fail[p];j&&!ch[j][i];j=fail[j]);
fail[v]=ch[j][i];
for(j=fail[v];j;j=fail[j])
cnt[j]++;
q.push(v);
}
}
}
int up(int now,int lenth){
while(lenth--)now=fa[now];
return now;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>c[i];
insert(c[i]);
}
getfail();
int ans=cn*cn;
for(int i=1;i<=cn;i++)
if(fail[i])ans-=cnt[up(i,dis[fail[i]])];
cout<<ans;
return 0;
}
T2:两个串
学了FFT再来看。
T3:K个串
题意:
求第K大的子串和。(以下和均为重复只算一次)
题解:
考虑每个点作为左端点对应的答案,将1为左端点时的答案:\(1\) 到 \(i\) 的和\((1\leq i\leq n)\),建一棵线段树,可以维护最大值。对于2为左端点的答案,发现只需要在第一颗线段树的基础上,将1节点改为-inf,并在[2,next[1]-1](next[i]表示i之后第一次出现a[i]的位置,没有出现则记为n+1)的节点全部减掉a[1]。后面的线段树都可以像这样构建,于是用可持久化线段树,有区间修改所以再写一个懒标记。
维护出每棵树后将他们的最大值插进堆里,取出其中最大值后,在对应线段树中删去最大值,也就是将对应节点改为-inf,再将此时的最大值插入堆中,循环k次就可以找到第K大值了。
时间复杂度大概是\(O((n+k)\log n)\),神犇hrj对它的常数略有研究,%%%。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
const int maxn=100005;
const int maxk=200005;
map<int,int> M;//仅用于求和时判重
int a[maxn],next[maxn];
int f[maxn];
priority_queue<pair<int,int> >Q;
//segment_tree----
#define ls(x) t[x].ls
#define rs(x) t[x].rs
#define mx(x) t[x].mx
#define tag(x) t[x].tag
int rt[maxn],tn=1;
struct node{
int ls,rs;
int mx,tag;
}t[maxn<<8];
int built(int l,int r,int p){//初始线段树
if(l==r){mx(p)=f[l];return p;}
int mid=(l+r)>>1;
ls(p)=built(l,mid,++tn);
rs(p)=built(mid+1,r,++tn);
mx(p)=max(mx(ls(p)),mx(rs(p)));
return p;
}
void cover(int &x,int d){//在x基础上新建节点
int y=++tn;
ls(y)=ls(x);
rs(y)=rs(x);
mx(y)=mx(x)+d;
tag(y)=tag(x)+d;
x=y;
}
void pushdown(int p){
if(!tag(p))return ;
cover(ls(p),tag(p));
cover(rs(p),tag(p));
tag(p)=0;
}
void make(int &now,int pre,int l,int r,int ml,int mr,int d){//now为新节点,pre为基础,l,r为范围,ml,mr为修改范围,d为加上的值,以这些数据新建线段树
now=++tn;
ls(now)=ls(pre);
rs(now)=rs(pre);
mx(now)=mx(pre);
tag(now)=tag(pre);
if(ml>mr)return ;
if(l>=ml&&r<=mr){
mx(now)+=d;
tag(now)+=d;
return ;
}
pushdown(now);
int mid=(l+r)>>1;
if(ml<=mid)make(ls(now),ls(now),l,mid,ml,mr,d);
if(mr>mid)make(rs(now),rs(now),mid+1,r,ml,mr,d);
mx(now)=max(mx(ls(now)),mx(rs(now)));
}
int getposition(int p,int l,int r){//找到这棵线段树中最大值的位置
if(l==r)return l;
int mid=(l+r)>>1;
if(mx(ls(p))>mx(rs(p)))return getposition(ls(p),l,mid);
else return getposition(rs(p),mid+1,r);
}
//----------------
signed main(){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
f[i]=f[i-1];
if(M.count(a[i]))next[M[a[i]]]=i;
else f[i]+=a[i];
M[a[i]]=i;
}
rt[1]=1;
built(1,n,1);
Q.push(make_pair(mx(rt[1]),1));
for(int i=2;i<=n;i++){
make(rt[i],rt[i-1],1,n,i,next[i-1]?next[i-1]-1:n,-a[i-1]);
make(rt[i],rt[i],1,n,i-1,i-1,0xb0b0b0b0b0b0b0b0LL);
Q.push(make_pair(mx(rt[i]),i));
}
int maxa;int kth;
for(int i=1;i<=k;i++){
maxa=Q.top().first;
kth=Q.top().second;
Q.pop();
int p=getposition(rt[kth],1,n);
make(rt[kth],rt[kth],1,n,p,p,-100000000000000000LL);
Q.push(make_pair(mx(rt[kth]),kth));
}
printf("%lld",maxa);
return 0;
}
21.6.21 test的更多相关文章
- 十二星座 英文名:Aries 金牛座 (4/21 - 5/20)的英文名: Taurus 双子座 (5/21 - 6/21)的英文名: Gemini 巨蟹座 (6/22 - 7/22)的英文名: Cancer 狮子座 (7/23 - 8/22)的英文名: Leo 处女座/室女座 (8/23 - 9/22)的英文名: Virgo 天秤座 (9/2
十二星座的具体顺序是:白羊座(Aries).金牛座(Taurus).双子座(Gemini).巨蟹座(Cancer).狮子座(Leo).处女座(Virgo).天秤座(Libra).天蝎座(Scorpio ...
- Android View事件机制 21问21答
原文: http://www.cnblogs.com/punkisnotdead/p/5179115.html#3358859 1.View的坐标参数 主要有哪些?分别有什么注意的要点? 答:Left ...
- 【Android】3.21 示例21—兴趣点收藏功能
分类:C#.Android.VS2015.百度地图应用: 创建日期:2016-02-04 简介:介绍如何创建.管理本地收藏的兴趣点数据 详述: (1)新建本地点收藏: (2)查看已收藏本地点: (3) ...
- 【转】ORACLE SQL基础—DDL语言 礼记八目 2017-12-23 21:26:21
原文地址:https://www.toutiao.com/i6502733303550837261/ SQL语言分为:DDL数据定义语言,DML数据操纵语言,DCL是数据库控制语言,TC事务控制语言 ...
- 记录21.07.21 —— ES6基础
学习目录 课件地址: ES6核心技术课件 1.let关键字 1.1 let与var的区别 ①let不能重复定义 ②块作用域的区别 ③变量声明之前let不能使用,var可以 ④ 课件代码 <htm ...
- 哈夫曼树-Fence Repair 分类: 树 POJ 2015-08-05 21:25 2人阅读 评论(0) 收藏
Fence Repair Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 32424 Accepted: 10417 Descri ...
- [转自老马的文章]用MODI OCR 21种语言
作者:马健邮箱:stronghorse_mj@hotmail.com发布:2007.12.08更新:2012.07.09按照<MODI中的OCR模块>一文相关内容进行修订2012.07.0 ...
- 用MODI OCR 21种语言
作者:马健邮箱:stronghorse_mj@hotmail.com发布:2007.12.08更新:2012.07.09按照<MODI中的OCR模块>一文相关内容进行修订2012.07.0 ...
- Java 学习(21):Java 实例
Java 实例 本章节我们将为大家介绍 Java 常用的实例,通过实例学习我们可以更快的掌握 Java 的应用. Java 环境设置实例 //HelloWorld.java 文件 public cla ...
随机推荐
- C# 动态构建表达式树(二)——构建 Select 和 GroupBy 的表达式
C# 动态构建表达式树(二)--构建 Select 和 GroupBy 的表达式 前言 在上篇中写了表达式的基本使用,为 Where 方法动态构建了表达式.在这篇中会写如何为 Select 和 Gro ...
- 自己实现一个Controller——精简型
写在最前 controller-manager作为K8S master的其中一个组件,负责众多controller的启动和终止,这些controller负责监控着k8s中各种资源,执行调谐,使他们的实 ...
- iNeuLink硬件网关与iNeuOS工业互联网操作系统互联互通应用案例
目 录 1. 应用概述... 2 2. 模拟硬件设备配置... 2 3. iNeuLink硬件网关配置... 4 3.1 硬件介绍... ...
- Rust之旅 02.通过例子学习自定义类型
本期文章接上期继续讲述Rust语言中的数据类型,Rust自定义数据类型主要是通过下面这两个关键字来创建: 结构体( struct ): 定义一个结构体(structure) 枚举( enum ): 定 ...
- 【敏捷0】敏捷项目管理-为什么从敏捷开始?为什么从PMI-ACP开始?
作为敏捷项目管理的开篇文章,还是先来简单地说一说为什么先从敏捷开始,为什么是以 PMI-ACP 为参考.当然,这一系列的文章可能不可避免地会为 PMI-ACP 做一些广告,但是我想告诉大家的是,敏捷以 ...
- TP5 windows中执行定时任务
1 首先先写个自定义命令文件 比如 Test 2 在网站根目录下建立文件 crond.bat ,内容:(把你在cmd上操作流程写一遍) D: cd workspace\wamp\tp5 D:\PHPW ...
- Java基础系列(4)- 编译型和解释型
概念 有一个外国人要看一本中文的书,有两种方式可以看,一种是把这本书翻译成英文版,另外一种是请一个中文翻译,想看哪边,翻译就翻译哪边. 针对上述的描述,翻译成英文版本的书籍对应的就是编译型,将代码编译 ...
- 基于Docker构建企业Jenkins CI平台
- javascript 无限分类
* 根据php无限分类实现js版本的 /** * 根节点 parentid=0, 每个节点都有id, parentid字段 * @param items * @returns {*} */ funct ...
- JPA自动生成表
一句话总结: 在配置文件中 jpa-hibernate-ddl-auto:update validate 加载 Hibernate 时,验证创建数据库表结构 create 每次加载 Hibernate ...