[JSOI2019]节日庆典 做题心得
[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]节日庆典 做题心得的更多相关文章
- [BJOI2016]水晶 做题心得
[BJOI2016]水晶 做题心得 这是一个写了我两小时的傻逼题.写这个题浪费了一堆时间后,我才意识到我码力又不行了.于是整理起了实现技巧,开始练码力. 思路 不难.首先把 \((x,y,z)\) 变 ...
- CF1416D 做题心得
CF1416D 做题心得 上次在某trick中提到了这个题,一开始觉得太毒瘤没有写,现在把它补上了. 感觉实现这个东西,比单纯收获一个trick,收获的东西多太多了. 主要思路 它的主要trick是& ...
- [NOIP补坑计划]NOIP2017 题解&做题心得
终于做完了…… 场上预计得分:?(省一分数线:295) 由于看过部分题解所以没有预计得分qwq 题解: D1T1 小凯的疑惑 题面 震惊!一道小学奥数题竟难倒无数高中考生! 欢迎大家以各种姿势*和谐* ...
- [NOIP补坑计划]NOIP2016 题解&做题心得
感觉16年好难啊QAQ,两天的T2T3是不是都放反了啊…… 场上预计得分:100+80+100+100+65+100=545(省一分数线280) ps:loj没有部分分,部分分见洛咕 题解: D1T1 ...
- [NOIP补坑计划]NOIP2015 题解&做题心得
感觉从15年开始noip就变难了?(虽然自己都做出来了……) 场上预计得分:100+100+60~100+100+100+100=560~600(省一分数线365) 题解: D1T1 神奇的幻方 题面 ...
- [NOIP补坑计划]NOIP2013 题解&做题心得
场上预计得分:100+100+100+100+100+60=560(省一分数线410) 五道傻逼题+一道大搜索题…… 题解: D1T1 转圈游戏 题面 水题送温暖~ #include<algor ...
- [NOIP补坑计划]NOIP2012 题解&做题心得
场上预计得分:100+90+70+100+100+3060=490520(省一分数线245) 题解: D1T1 Vigenère 密码 题面 水题送温暖~~ #include<iostream& ...
- [NOIP补坑计划]NOIP2014 题解&做题心得
六道普及组题,没啥好说的 场上预计得分:100+100+100+100+100+100=600(省一分数线490) (AK是不可能AK的,这辈子不可能AK的) 题解: D1T1 生活大爆炸版石头剪刀布 ...
- [JSOI2019]节日庆典(Z-algorithm)
要想让一个位置作为最小循环,其必须是最小后缀,然后一个字符串的最小后缀不超过O(logn)个,于是维护备选集合即可. 然而要在O(n)复杂度求解,需要求出原串后缀与原串的LCP长度,需要用Z-algo ...
随机推荐
- jsp中将一个jsp引入另一个jsp指定位置
<jsp:include page="badSurveyUpdate.jsp"/>
- Turtlebot3新手教程:Open-Manipulator机械臂
*本文针对如何结合turtlebot3和Open-Manipulator机械臂做出讲解 测试在Ubuntu 16.04, Linux Mint 18.1和ROS Kinetic Kame下进行 具体步 ...
- c3p0连接池使用:使用c3p0数据源步骤以及完成jdbcUtills类
1.使用c3p0数据源步骤): a.下载c3p0jar,官网下载:https://sourceforge.net/projects/c3p0/: b.导入jar包时,应该导入下面两个包: c.编写c3 ...
- 线程专题 -- 线程池,ThreadPoolExecutor
什么是线程池? 为什么要使用它? 线程池是为了避免线程频繁的创建和销毁带来的性能消耗,而建立的一种池化技术,它是把已创建的线程放入"池"中,当有任务来临时就可以重用已有的线程,无需 ...
- 【函数分享】每日PHP函数分享(2021-1-9)
implode() 将一个一维数组的值转化为字符串. string implode ( string $glue , array $pieces ) 参数描述 glue 默认为空的字符串. p ...
- Popup中ListBox的SelectChange事件关闭弹出窗体后主窗体点击无效BUG
WPF的BUG!弹出框的 自定义控件里有Popup, Popup里面放一个ListBox 在ListBox中的SelectionChange事件触发关闭弹出框后,主窗体存在一定概率卡死(但点击标题又能 ...
- C语言指针的大小
C语言指针的大小 今天看到一道题目是这样的,写出以下变量在32位设备上的大小(占多少个字节) 然后其中就有一些指针类型的数据,那么我们知道在C语言中指针的大小都是一样的,不管是有数据类型的还是void ...
- DevOps,CI,CD,自动化简单介绍
前言: 随着企业应用的不断迭代,不断扩大,应用的发布发布可能涉及多个团队,如pc端,手机端,小程序端等等.应用发布也就成为了一项高风险,高压力的超过过程,以及应用的开发迭代的沟通,测试成本也大大的变得 ...
- 【渲染引擎】Blender的2021年最佳渲染引擎(上)
Blender最终摆脱了"古怪的孩子"的装束,并穿上了更为严肃和受人尊敬的" 3D强者". 它已在业界获得广泛认可,许多工作室和艺术家正在将其纳入他们的产品线. ...
- 直播预告:Quadro RTX显卡助力Twinmotion在建筑表现领域火力全开
新年伊始,泛CG继续起航! 2021年首期泛CG分享会 我们邀请了两位业界大咖一起分享 NVIDIA GPU实时渲染在建筑可视化领域的应用 新的一年,继续相约! 1.嘉宾介绍 魏老师从事设计可视化工作 ...