luogu2282/bzoj1219 历史年份 (dp+hash+二分+线段树)
luogu1415 拆分数列的加强版
先考虑弱化版怎么做
设f[i]表示某一串数,最后一个数的右端点是i时,它的左端点的最大值(也就是说,这一串数的最后一个数尽量小)
那么有$f[j]=max\{i+1|num[i+1,j]>num[f[i],i]\}$
这样推下去,f[N]就是最后一个数的最小值
然后我们把它钦定住,再用类似的方式推回来,算出来最前面数的最大值
直接做的话,转移$O(n)$,判断两数是否相等$O(n)$,所以总共是$O(n^3)$的
显然过不了加强版,考虑如何优化。
可以发现这个转移其实是固定左端点,找到一些右端点,使得这个数比当前的数大,那么只要找到第一个比它大的右端点,后面的就都比它大
而且如果不考虑前缀0的话,恰好比它大的那个子串,长度就要么和它相等、要么是它的+1
所以只需要找出来那个长度和它相等的子串,比较他俩的大小,要是比它小就加个1,然后把后面的都更新掉
直接判定是$O(n)$的,但用hash先$O(n)$预处理,就可以做到$O(logn)$,具体做法是二分找出两个子串的LCP,再判断LCP+1的大小
然后更新的时候用一个线段树维护区间取max、单点查询,也可以做到$O(logn)$
所以复杂度$O(Tnlogn)$
然而还有前缀0的问题。这样的话,我们就不能直接找和他长度相等的子串,而是要找去掉前缀0以后和它去掉前缀0以后长度相等的子串
可以记一个nn0[i]表示i位置后面的第一个不是0的位置,pn0[i]表示i位置前面的第一个不是0的位置然后乱搞
倒着做回来的时候都类似,然后我定义的g[i]是表示以i为左端点的最大右端点,也需要很注意前缀0的问题
(有可能我发现长度相等的那个比它小,然后想往前顶一个,这时候应该顶到前面的第一个不为0的位置,否则加一个前缀0还是比它小)
注意g的初值应该是g[pn0[f[N]]+1~f[N]]=N,因为你找到的那个最小值 加上一些前缀0还是最小值,这些都是合法的
需要(?)特判全都是0的情况
代码改来改去的写的很丑
(不知道为什么,洛谷上开O2第一个点会OLE)
#include<bits/stdc++.h>
#define pa pair<int,int>
#define CLR(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=,P=; inline ll rd(){
ll x=;char c=getchar();int neg=;
while(c<''||c>''){if(c=='-') neg=-;c=getchar();}
while(c>=''&&c<='') x=x*+c-'',c=getchar();
return x*neg;
} char num[maxn];
int N,M,f[maxn],nn0[maxn],pn0[maxn];
int laz[maxn<<],ma[maxn<<];
ull hsh[maxn],bin[maxn]; inline bool bigger(int x1,int x2,int l){
int a=,b=l,k=;
while(a<=b){
int m=a+b>>;
if(hsh[x1+m-]-hsh[x1-]*bin[m]==hsh[x2+m-]-hsh[x2-]*bin[m]) a=m+,k=m;
else b=m-;
}
if(k>=l) return ;
else return num[x1+k]>num[x2+k];
return ;
} inline void pushdown(int p){
if(!laz[p]) return;
int a=p<<,b=p<<|;
ma[a]=max(laz[p],ma[a]),ma[b]=max(laz[p],ma[b]);
laz[a]=max(laz[a],laz[p]),laz[b]=max(laz[b],laz[p]);
laz[p]=;
} inline void change(int p,int l,int r,int x,int y,int z){
ma[p]=max(ma[p],z);
if(x<=l&&r<=y){
laz[p]=max(laz[p],z);
}else{
pushdown(p);
int m=l+r>>;
if(x<=m) change(p<<,l,m,x,y,z);
if(y>=m+) change(p<<|,m+,r,x,y,z);
}
} inline int query(int p,int l,int r,int x){
if(l==r) return ma[p];
int m=l+r>>;
pushdown(p);
if(x<=m) return query(p<<,l,m,x);
else return query(p<<|,m+,r,x);
} int main(){
//freopen(".in","r",stdin);
int i,j,k;
bin[]=;for(i=;i<=;i++) bin[i]=bin[i-]*P;
while(~scanf("%s",num+)){
N=strlen(num+);
for(i=;i<=N;i++)
hsh[i]=hsh[i-]*P+num[i];
CLR(ma,);CLR(laz,);
nn0[N+]=N+;
for(i=N;i>=;i--)
nn0[i]=(num[i+]!='')?i+:nn0[i+];
for(i=;i<=N;i++)
pn0[i]=(num[i-]!='')?i-:pn0[i-];
change(,,N,,N,);
for(i=;i<=N;i++){
f[i]=query(,,N,i);
int y=nn0[i],nxt=y+i-nn0[f[i]-]+;
if(bigger(y,nn0[f[i]-],i-nn0[f[i]-]+)) nxt--;
if(nxt<=N) change(,,N,nxt,N,i+);
}
M=f[N];
CLR(f,);CLR(ma,);CLR(laz,);
change(,,N,pn0[M]+,M,N);
for(i=M;i;i--){
f[i]=query(,,N,i);
int y=nn0[i-(f[i]-nn0[i-]+)-],nxt;
if(i-y<f[i]-nn0[i-]+||(i-y==f[i]-nn0[i-]+&&bigger(nn0[i-],y,f[i]-nn0[i-]+))) nxt=pn0[y]+;
else nxt=y+;
if(nxt<=i-) change(,,N,nxt,i-,i-);
}
if(N<nn0[]) printf("%s",num+);
else{
for(i=;i<=N;i=f[i]+){
for(j=i;j<=f[i];j++)
putchar(num[j]);
if(f[i]!=N) putchar(',');
}
}printf("\n");
}
return ;
}
luogu2282/bzoj1219 历史年份 (dp+hash+二分+线段树)的更多相关文章
- HDU4614 Vases and Flowers 二分+线段树
分析:感觉一看就是二分+线段树,没啥好想的,唯一注意,当开始摆花时,注意和最多能放的比大小 #include<iostream> #include<cmath> #includ ...
- J - Joseph and Tests Gym - 102020J (二分+线段树)
题目链接:https://cn.vjudge.net/contest/283920#problem/J 题目大意:首先给你n个门的高度,然后q次询问,每一次询问包括两种操作,第一种操作是将当前的门的高 ...
- Educational Codeforces Round 61 D 二分 + 线段树
https://codeforces.com/contest/1132/problem/D 二分 + 线段树(弃用结构体型线段树) 题意 有n台电脑,只有一个充电器,每台电脑一开始有a[i]电量,每秒 ...
- 【BZOJ-3110】K大数查询 整体二分 + 线段树
3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 6265 Solved: 2060[Submit][Sta ...
- hdu6070 Dirt Ratio 二分+线段树
/** 题目:hdu6070 Dirt Ratio 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6070 题意:给定n个数,求1.0*x/y最小是多少.x ...
- K-th occurrence HDU - 6704 (后缀数组+二分线段树+主席树)
大意: 给定串s, q个询问(l,r,k), 求子串s[l,r]的第kk次出现位置. 这是一篇很好的题解: https://blog.csdn.net/sdauguanweihong/article/ ...
- lightoj 1084 - Winter(dp+二分+线段树or其他数据结构)
题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1084 题解:不妨设dp[i] 表示考虑到第i个点时最少有几组那么 if a[i ...
- 2019杭电多校第三场hdu6606 Distribution of books(二分答案+dp+权值线段树)
Distribution of books 题目传送门 解题思路 求最大值的最小值,可以想到用二分答案. 对于二分出的每个mid,要找到是否存在前缀可以份为小于等于mid的k份.先求出这n个数的前缀和 ...
- 4.11 省选模拟赛 序列 二分 线段树优化dp set优化dp 缩点
容易想到二分. 看到第一个条件容易想到缩点. 第二个条件自然是分段 然后让总和最小 容易想到dp. 缩点为先:我是采用了取了一个前缀最小值数组 二分+并查集缩点 当然也是可以直接采用 其他的奇奇怪怪的 ...
随机推荐
- centos 6.9:device eth0 does not seem to be present
VMware上安装centos6.9,克隆一个新虚机,网卡不能桥接获得宿主机网络地址. https://blog.csdn.net/xiaobei4929/article/details/405152 ...
- Oracle SQL优化原则
原文:http://bbs.landingbj.com/t-0-240353-1.html 1.选用适合的 ORACLE 优化器 2.访问 Table 的方式 3.共享SQL语句 共享的语句必须满足三 ...
- 理解ORM的前提:数据库中的范式和约束
理解ORM的前提:数据库中的范式和约束 一.数据库中的范式: 范式, 英文名称是 Normal Form,它是英国人 E.F.Codd(关系数据库的老祖宗)在上个世纪70年代提出关系数据库模型后总结出 ...
- Git使用:Linux(Ubuntu 14.04 x64)下安装Git并配置连接GitHub
github是一个非常好的网络代码托管仓库,知晓许久,但是一直没有用起来,最近才开始使用git管理自己的文档和代码. Git是非常强大的版本管理工具,今天就告诉大家,如何在Linux下安装GIt,并且 ...
- Sqoop 使用详解(内含对官方文档的解析)
Sqoop 是 Cloudera 公司创造的一个数据同步工具,现在已经完全开源了. 目前已经是 hadoop 生态环境中数据迁移的首选,另外还有 ali 开发的 DataX 属于同类型工具,由于社区的 ...
- 让PC端页面在手机端显示缩小版的解决方法
做页面的时候我们做好pC端页面时,因编辑那边需求,在手机端页面也应该是缩小版,不能乱的.在网上找了各种解决方案,经实验,这种是可以的: 在head里边加上这两句meta <meta name= ...
- python设计模式第二十二天【备忘录模式】
1.应用场景 (1)能保存对象的状态,并能够恢复到之前的状态 2.代码实现 #!/usr/bin/env python #! _*_ coding:UTF-8 _*_ class Originator ...
- Java8 flatMap的sample
外国人写得, 很不错 http://www.java67.com/2016/03/how-to-use-flatmap-in-java-8-stream.html package test; impo ...
- Lodop打印控件设置表格次页偏移
Lodop打印控件有很好的自动分页功能,超文本table表格一页装不下,自动分到第二页,第三页……通常表格之前还会有一些内容,比如标题,制表人名称日期什么的杂七杂八的东西,这种东西会占用一定的空间,这 ...
- CentOS 7 vi编辑命令
用vi打开一个yum文件 vi /usr/bin/yum 按 i 键后 进入insert模式,进入insert模式后才能进行修改 修改完成后 按esc键进入command模式, 然后:wq 保存文件 ...