[BZOJ5351]Query on a sequence
[BZOJ5351]Query on a sequence
题目大意:
给定一个长度为\(n(n\le10^5)\)的数列\(P\),满足\(|P_i|\le10^9\),求满足下列约束的不同的四元组\((a,b,c,d)\)的个数:
- \(1\le a\le b<c\le d\le n\);
- \(b-a=d-c\);
- \(c-b-1=m\),\(m(m>0)\)为给定的数;
- \(p_{a+i}=P_{c+i}\)对于所有\(i(0\le i\le b-a)\)均成立。
思路:
对于\(a\)和\(c\),若\(c-a>m\),且对于后缀\(S_a\)和\(S_c\),有\(\operatorname{lcp}(S_a,S_c)\ge c-a-m\),则我们可以求出合法的\(b\)和\(d\)的区间。
首先对于数列建立后缀数组。从大到小枚举\(lcp[i]\),合并其对应的两个后缀\(a\)和\(c\),因为若下一条枚举到的\(lcp[j]\)如果是关于\(c\)的,则由于\(lcp[i]\ge lcp[i]\),它一样可以适用于\(a\)。对于每个连通块用线段树维护数列每个区间内的数开头的每个后缀是否出现。统计时枚举连通块内每一个后缀,枚举它是\(a\)还是\(c\),在线段树上查找区间和即可。
源代码:
#include<cstdio>
#include<cctype>
#include<climits>
#include<algorithm>
inline int getint() {
register char ch;
register bool neg=false;
while(!isdigit(ch=getchar())) neg|=ch=='-';
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=1e5+1;
int n,m,k,s[N],sa[N],rank[N],tmp[N];
std::pair<int,int> lcp[N];
inline bool cmp(const int &i,const int &j) {
if(rank[i]!=rank[j]) return rank[i]<rank[j];
const int ri=i+k<=n?rank[i+k]:-1;
const int rj=j+k<=n?rank[j+k]:-1;
return ri<rj;
}
inline void suffix_sort() {
for(register int i=0;i<=n;i++) {
sa[i]=i;
rank[i]=s[i];
}
for(k=1;k<=n;k<<=1) {
std::sort(&sa[0],&sa[n+1],cmp);
tmp[sa[0]]=0;
for(register int i=1;i<=n;i++) {
tmp[sa[i]]=tmp[sa[i-1]]+!!cmp(sa[i-1],sa[i]);
}
std::copy(&tmp[0],&tmp[n]+1,rank);
}
};
inline void init_lcp() {
for(register int i=0,h=0;i<n;i++) {
if(h>0) h--;
const int &j=sa[rank[i]-1];
while(i+h<n&&j+h<n&&s[i+h]==s[j+h]) h++;
lcp[rank[i]-1]=std::make_pair(-h,rank[i]);
}
}
const int SIZE=N*20;
class SegmentTree {
private:
struct Node {
int val,left,right;
};
Node node[SIZE];
int sz,new_node() {
return ++sz;
}
public:
int root[N];
void insert(int &p,const int &b,const int &e,const int &x) {
node[p=new_node()].val++;
if(b==e) return;
const int mid=(b+e)>>1;
if(x<=mid) insert(node[p].left,b,mid,x);
if(x>mid) insert(node[p].right,mid+1,e,x);
}
int query(const int &p,const int &b,const int &e,const int &l,const int &r) const {
if(r<l) return 0;
if(b==l&&e==r) return node[p].val;
const int mid=(b+e)>>1;
int ret=0;
if(l<=mid) ret+=query(node[p].left,b,mid,l,std::min(mid,r));
if(r>mid) ret+=query(node[p].right,mid+1,e,std::max(mid+1,l),r);
return ret;
}
int merge(const int &x,const int &y) {
if(!x||!y) return x|y;
node[y].val+=node[x].val;
node[y].left=merge(node[x].left,node[y].left);
node[y].right=merge(node[x].right,node[y].right);
return y;
}
};
SegmentTree t;
struct DisjointSet {
int anc[N],min[N],size[N];
int find(const int &x) {
return x==anc[x]?x:anc[x]=find(anc[x]);
}
void reset() {
for(register int i=1;i<=n;i++) {
size[i]=1;
anc[i]=min[i]=i;
}
}
void merge(const int &x,const int &y) {
anc[x]=y;
min[y]=std::min(min[x],min[y]);
size[y]+=size[x];
}
};
DisjointSet djs;
int lim,ans;
inline void merge(int x,int y) {
x=djs.find(x),y=djs.find(y);
if(djs.size[x]>djs.size[y]) std::swap(x,y);
for(register int i=djs.min[x];i<djs.min[x]+djs.size[x];i++) {
ans+=t.query(t.root[y],1,n,std::max(sa[i]+1+m+1,1),std::min(sa[i]+1+lim,n));
ans+=t.query(t.root[y],1,n,std::max(sa[i]+1-lim,1),std::min(sa[i]+1-m-1,n));
}
djs.merge(x,y);
t.merge(t.root[x],t.root[y]);
}
int main() {
n=getint(),m=getint();
for(register int i=0;i<n;i++) s[i]=getint();
s[n]=INT_MIN;
suffix_sort();
init_lcp();
std::sort(&lcp[0],&lcp[n]);
for(register int i=1;i<=n;i++) {
t.insert(t.root[i],1,n,sa[i]+1);
}
djs.reset();
for(register int i=0;i<n;i++) {
lim=-lcp[i].first+m;
merge(lcp[i].second,lcp[i].second-1);
}
printf("%d\n",ans);
return 0;
}
[BZOJ5351]Query on a sequence的更多相关文章
- C#/.NET Little Wonders: Use Cast() and OfType() to Change Sequence Type(zz)
Once again, in this series of posts I look at the parts of the .NET Framework that may seem trivial, ...
- HDU 4441 Queue Sequence(优先队列+Treap树)(2012 Asia Tianjin Regional Contest)
Problem Description There's a queue obeying the first in first out rule. Each time you can either pu ...
- LinqToDB 源码分析——生成与执行SQL语句
生成SQL语句的功能可以算是LinqToDB框架的最后一步.从上一章中我们可以知道处理完表达式树之后,相关生成SQL信息会被保存在一个叫SelectQuery类的实例.有了这个实例我们就可以生成对应的 ...
- LinqToDB 源码分析——处理表达式树
处理表达式树可以说是所有要实现Linq To SQL的重点,同时他也是难点.笔者看完作者在LinqToDB框架里面对于这一部分的设计之后,心里有一点不知所然.由于很多代码没有文字注解.所以笔者只能接合 ...
- Elven Postman(BST )
Elven Postman Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)T ...
- of
“查询序列的一个元素” 1. an element of the query sequence (T) 2. an query sequence element (T) "查询序列或者候选序 ...
- hdu 5444 Elven Postman 二叉树
Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Problem Descrip ...
- Regional Changchun Online--Elven Postman(裸排序二叉树)
Elven Postman Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Tot ...
- hdu 5444 Elven Postman
题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5444 Elven Postman Description Elves are very peculia ...
随机推荐
- Less & Sass
CSS不是一种编程语言.它开发网页样式,但是没法用它编程.也就是说,CSS基本上是设计师的工具,它没有变量,也没有条件语句,只是一行行单纯的描述.有人就开始为CSS加入编程元素,这被叫做"C ...
- linux中使用mysql数据库
在安装完数据库后,如果没有设置root的mysql密码,在命令行输入mysql即可进入数据库 show databases;(有分号):查看当前存在的数据库 create database 名字:创建 ...
- 安全测试===burpsuit指南
网址: https://www.gitbook.com/book/t0data/burpsuite/details 引子 刚接触web安全的时候,非常想找到一款集成型的渗透测试工具,找来找去,最终选择 ...
- 【uva11613】生产销售规划
这很像之前做的一道noip模拟题…… 所以当时那题也可以用费用流写(雾) 拆点,将每个月拆成两个点,一个向起点连边表示产量,另一个点连汇点表示销量. 然后每个点依次往后面的点2连边,表示保存. #in ...
- iOS 适配/ autoLayout基本知识
历史 iPhone3GS.iPhone4\4s:没有屏幕适配最早开发里面的程序全部都是写死的 iPad 旋转出来之后 Autoresizing问世iPhone5\5c\5s兼容各种不同的情况 系统适配 ...
- 坐标转换——GCJ-02
WGS84(World Geodetic System 1984),是为GPS 全球定位系统 使用而建立的坐标系统GCJ-02,我国在WGS84的基础上加密得到BD-09,百度坐标在GCJ-02基础上 ...
- Eclipse+Pydev+numpy+scipy+matplotlib
之前一直在linux环境下使用python,作为一枚小菜还是更喜欢windows.我使用python主要是进行科学计算,安装软件.搭建环境遇到了非常多的问题,特此总结. 一.python安装 版本:2 ...
- django “如何”系列3:如何编写模型域(model filed)
django自带很多的域类--CharField,DateField等等--,如果django的这些域都不能满足你精确的要求,那么你可以编写自己的模型域. django自带的域没有和数据库列类型一一对 ...
- 7:django 中间件
中间件 中间件是一个连接django请求/相应处理的框架,是一个轻量级的低层次的全局影响django输入输出的系统插件. 每一个中间件组件负责一些特定的功能,这里我们我们只看一下如何激活使用系统自带的 ...
- 【python】多个文件共用日志系统的重复打印问题
先写一个最简单的log文件: test_logging5.py #coding:utf-8 import logging logging.debug('logger debug message') l ...