[JSOI2019]节日庆典 做题心得

一个性质有趣的字符串题

这要是在考场上我肯定做不出来吧

一开始还以为要 SAM 什么的暴力搞,没想到只用到了 \(Z\) 函数 —— 也是我生疏了罢

(学了啥忘了啥,这可怎么去wc啊啊啊

思路

考虑维护候选集合 \(S\),表示这个位置 可能 是最优位置。

假设我们可以知道 \(S\),拿 \(Z\) 函数求一下 \(LCP\) 就可以知道是不是最优的了。

大胆猜测一个性质,要么可以快速的找 \(S\) 中最小的位置,要么 \(S\) 的大小不会很大,可以暴力找。

第一个思路不太好搞,那看来就选第二个思路吧

假设现在考虑前 \(k\) 位

首先可以来一个优化:如果还没循环,就比出来了胜负,那么大的那个一定不会是最优解 —— 并且随着 \(k\) 的增加,显然不会翻身成更优的

那现在对于 \(i,j\in S,i<j\),一定有 \(LCP(s[i:k],s[j:k])= k-j+1\)。

考虑两个 \(i,j\) ,如果 \([i,i+LCP-1]\) 和 \([j,j+LCP-1]\) 不重合,看来没什么性质

如果重合,似乎有些有趣的性质,先放结论:

如果重合,即 \(k-j+1>j-i\),则 \(j\) 一定不会是最优的

我相信这个图还挺清楚的

红色是 \(i,j\) 的 \(LCP\)。然后设 \(i\) 到 \(j\) 前面是 \(A=s[i:j-1]\),然后红色减去 \(A\) 之后剩下的是 \(B=s[j:i+LCP-1]\),是从 \(j\) 开始的。由于两个红色相等,所以 \(B\) 是红色的前缀,对应到后面,它也是红色的后缀(即它是红色的 border)

\(p\) 是 \(j\) 往后数一个 \(A\) 之后的开始位置

设 \(i\) 前面的是 \(C\)。\(i\) 开始的循环串就是 \(AABC\),\(j,p\) 同理

然后发现 \(BC\) 作为一个整体出现,把它看成 \(D\)。然后就清楚多了:

如果 \(D<A\),那么 \(p\) 开始是最小的;

如果 \(A<D\),那么 \(i\) 开始是最小的;

如果 \(A=D\),那么三个一样大;

总之,忽略 \(j\) ,对最大值没有影响

那么 \(S\) 集合中,每往前数一个数,它到上一个的距离,都比上一个到 \(k\) 的距离大。和启发式合并类似的,有一个基本事实:

一个数加上比它大的数,至少翻一倍

那 \(S\) 集合中数到 \(k\) 的距离,每次至少翻一倍,所以 \(S\) 的大小是 \(\log\) 的。

每次新加入元素的时候维护一下即可,然后分段考虑一下,求最小的那个开始位置。

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 10000007
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
#define Tra(i,u) for(int i=G.st(u),v=G.to(i);~i;i=G.nx(i),v=G.to(i))
#define p_b push_back
#define sz(a) ((int)a.size())
#define all(a) a.begin(),a.end()
#define iter(a,p) (a.begin()+p) int n;
char s[N];
void Input()
{
scanf("%s",s+1);
n=strlen(s+1);
}
int z[N];
void Z_Function()
{
z[1]=n; F(i,2,n) z[i]=0;
int l=0,r=0;
F(i,2,n)
{
if (i<=r) z[i]=min(z[i-l+1],r-i+1);
while(i+z[i]<=n and s[i+z[i]]==s[z[i]+1]) ++z[i];
if (i+z[i]-1>=r) l=i,r=i+z[i]-1;
}
}
int c[N],cp=0;
int stk[N],top=0;
void fuckoff(int k)
{
while(top) stk[top--]=0;
F(i,1,cp)
{
int x=c[i];
while(top and s[stk[top]+k-x]>s[k])
{
--top;
}
if (top and s[stk[top]+k-x]<s[k]) continue;
stk[++top]=x;
}
F(i,1,cp) c[i]=0; cp=top; F(i,1,top) c[i]=stk[i]; while(top) stk[top--]=0;
D(i,cp,1)
{
while(top and k-stk[top]+1>stk[top]-c[i]) --top;
stk[++top]=c[i];
}
F(i,1,cp) c[i]=0; cp=top; F(i,1,top) c[i]=stk[top-i+1];
}
int getmin(int k)
{
int ansp=c[1];
F(i,2,cp)
{
int x=c[i];
// printf("x=%d cur=%d\n",x,ansp);
int LCP1=k-x+1;
int LCP2=min(z[ansp+LCP1],k-(ansp+LCP1)+1);
// printf(" LCP2=%d\n",LCP2);
if (LCP2<k-(ansp+LCP1)+1)
{
int p1=ansp+LCP1+LCP2;
int p2=LCP2+1;
// printf(" case2 p1=%d p2=%d\n",p1,p2);
if (p2>k) continue;
if (s[p1]>s[p2])
{
// puts(" better");
ansp=x;
}
}
else
{
int LCP3=min(z[LCP2+1],k-LCP2);
int p1=LCP3+1;
int p2=LCP2+LCP3+1;
// printf(" LCP3=%d\n",LCP3);
// printf(" case3 p1=%d p2=%d\n",p1,p2);
if (p2>k) continue;
if (s[p1]>s[p2])
{
// puts(" better");
ansp=x;
}
}
}
return ansp;
}
void Sakuya()
{
Z_Function();
F(i,1,n)
{
c[++cp]=i;
fuckoff(i);
printf("%d ",getmin(i));
}
puts("");
}
void IsMyWife()
{
Input();
Sakuya();
}
}
#undef int //long long
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();
return 0;
}

[JSOI2019]节日庆典 做题心得的更多相关文章

  1. [BJOI2016]水晶 做题心得

    [BJOI2016]水晶 做题心得 这是一个写了我两小时的傻逼题.写这个题浪费了一堆时间后,我才意识到我码力又不行了.于是整理起了实现技巧,开始练码力. 思路 不难.首先把 \((x,y,z)\) 变 ...

  2. CF1416D 做题心得

    CF1416D 做题心得 上次在某trick中提到了这个题,一开始觉得太毒瘤没有写,现在把它补上了. 感觉实现这个东西,比单纯收获一个trick,收获的东西多太多了. 主要思路 它的主要trick是& ...

  3. [NOIP补坑计划]NOIP2017 题解&做题心得

    终于做完了…… 场上预计得分:?(省一分数线:295) 由于看过部分题解所以没有预计得分qwq 题解: D1T1 小凯的疑惑 题面 震惊!一道小学奥数题竟难倒无数高中考生! 欢迎大家以各种姿势*和谐* ...

  4. [NOIP补坑计划]NOIP2016 题解&做题心得

    感觉16年好难啊QAQ,两天的T2T3是不是都放反了啊…… 场上预计得分:100+80+100+100+65+100=545(省一分数线280) ps:loj没有部分分,部分分见洛咕 题解: D1T1 ...

  5. [NOIP补坑计划]NOIP2015 题解&做题心得

    感觉从15年开始noip就变难了?(虽然自己都做出来了……) 场上预计得分:100+100+60~100+100+100+100=560~600(省一分数线365) 题解: D1T1 神奇的幻方 题面 ...

  6. [NOIP补坑计划]NOIP2013 题解&做题心得

    场上预计得分:100+100+100+100+100+60=560(省一分数线410) 五道傻逼题+一道大搜索题…… 题解: D1T1 转圈游戏 题面 水题送温暖~ #include<algor ...

  7. [NOIP补坑计划]NOIP2012 题解&做题心得

    场上预计得分:100+90+70+100+100+3060=490520(省一分数线245) 题解: D1T1 Vigenère 密码 题面 水题送温暖~~ #include<iostream& ...

  8. [NOIP补坑计划]NOIP2014 题解&做题心得

    六道普及组题,没啥好说的 场上预计得分:100+100+100+100+100+100=600(省一分数线490) (AK是不可能AK的,这辈子不可能AK的) 题解: D1T1 生活大爆炸版石头剪刀布 ...

  9. [JSOI2019]节日庆典(Z-algorithm)

    要想让一个位置作为最小循环,其必须是最小后缀,然后一个字符串的最小后缀不超过O(logn)个,于是维护备选集合即可. 然而要在O(n)复杂度求解,需要求出原串后缀与原串的LCP长度,需要用Z-algo ...

随机推荐

  1. 图解 Java 数据结构

    图解Java数据结构: 一.链表       Java ListNode     https://www.cnblogs.com/easyidea/p/13371863.html 二.栈       ...

  2. maven版本仲裁原则

    这里有一个案例是项目里依赖了b组件,b组件依赖了a组件1.0.2版本,而用户也直接在pom依赖了a组件并声明的1.0.0版本,结果在仲裁时选择了1.0.0版本的a组件: +- com.xxx:a:ja ...

  3. 基于jupyter lab搭建网页编程环境并添加自定义python kernel和matlab kernel以及plotly的使用

    内容转载自我的博客 目录 说明 1. 创建虚拟环境jupyter 2. 安装nodejs(用于jupyterlab安装扩展) 3. 安装pip包 4. 使用jupyterlab 5. 配置jupyte ...

  4. hbase读写优化

    一.hbase读优化 客户端优化 1.scan缓存是否设置合理? 优化原理:一次scan请求,实际并不会一次就将所有数据加载到本地,而是多次RPC请求进行加载.默认100条数据大小. 优化建议:大sc ...

  5. Head First 设计模式 —— 05. 单例模式

    全局变量的缺点 如果将对象赋值给一个全局变量,那么必须在程序一开始就创建好对象 P170 和 JVM 实现有关,有些 JVM 的实现是:在用到的时候才创建对象 思考题 Choc-O-Holic 公司使 ...

  6. 项目实战--解决运行sql文件错误

    说明: 新项目启动,通过公司运维同学给的数据库脚本在Navicat中建项目的数据库,运行脚本时报错 Error Code: 1227. Access denied; you need (at leas ...

  7. 使用 Flux,Helm v3,Linkerd 和 Flagger 渐进式交付 Kubernetes

    介绍 本指南将引导您在 Kubernetes 集群上设置渐进式交付 GitOps 管道. GitOps Helm 研讨会 原文地址:GitOps Progressive Deliver with Fl ...

  8. day122:MoFang:OSSRS流媒体直播服务器&基于APICloud的acLive直播推流模块实现RTMP直播推流

    目录 1.docker安装OSSRS流媒体直播服务器 2.基于APICloud的acLive直播推流模块实现RTMP直播推流 3.直播流管理 1.docker安装OSSRS流媒体直播服务器 1.OSS ...

  9. 【递归】P1157组合的输出

    题目相关 题目描述 排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且 r ≤n),我们可以简单地将n个元素理解为自然数1,2,-,n从中任取r个数. 现要求你输出所有组合. ...

  10. HP Proliant DL580 gen9 阵列卡P440AR 高速缓存 被禁用

    摘录内容: IMPORTANT: This issue does NOT occur when the operating system shuts down gracefully. In addit ...