【刷题】BZOJ 4698 Sdoi2008 Sandy的卡片
Description
Sandy和Sue的热衷于收集干脆面中的卡片。然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型。每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型,首先必须要集够N张卡片,对于这N张卡片,如果他们都有一个相同的子串长度为k,则可以兑换一个等级为k的人物模型。相同的定义为:两个子串长度相同且一个串的全部元素加上一个数就会变成另一个串。Sandy的卡片数远远小于要求的N,于是Sue决定在Sandy的生日将自己的卡片送给Sandy,在Sue的帮助下,Sandy终于集够了N张卡片,但是,Sandy并不清楚他可以兑换到哪个等级的人物模型,现在,请你帮助Sandy和Sue,看看他们最高能够得到哪个等级的人物模型。
Input
第一行为一个数N,表示可以兑换人物模型最少需要的卡片数,即Sandy现在有的卡片数
第i+1行到第i+N行每行第一个数为第i张卡片序列的长度Mi,之后j+1到j+1+Mi个数,用空格分隔,分别表示序列中的第j个数
n<=1000,M<=1000,2<=Mi<=101
Output
一个数k,表示可以获得的最高等级。
Sample Input
2
2 1 2
3 4 5 9
Sample Output
2
Solution
弄清一下数据范围,\(n \leq 1000,M_i \leq 101 ,\) 序列中的每一个数 \(\leq 1864\) ,不存在那个什么 \(M\)
然后这道题做法很多,挑了一种比较难写的做法,后缀数组+ST表+twopoints
先将所有序列中的每一个数减去它前面那个数,得到数列的趋势。那么最后要求的就变成了 \(n\) 个串的最长公共子串(但子串的第一个位置不一定需要相同)
将所有序列首尾相连拼在一起,做一遍 \(SA\) ,最后要求的就是在 \(SA\) 中找一段区间,使得对于每一个原序列,这一段区间中的后缀至少有一个是开头于这个原序列中的某个位置,然后要使这个区间中的 \(LCP\) 最大
由于 \(height\) 的性质 \(LCP(j,k)=\min_{i=rank[j]+1}^{rank[k]}height[i]\) ,所以区间越小答案肯定越优,至少不会变差,所以我们就只需要枚举区间右端点,用twopoints维护左端点就行了,然后使用预处理的 \(height\) 的ST表快速求区间中的 \(LCP\)
考虑几个细节:
- 有可能某个后缀的 \(LCP\) 的长度大于了它的开头到它属于的序列的结尾的长度,即这个后缀 \(LCP\) 延伸到它属于的序列的后面那个序列去了。这种情况本来是要对序列长度取min的,但是我们在拼接的时候,把序列与序列中间加上一个非常大的数(每两个序列中间的数也不同),那么这些情况直接就不会考虑了
- 由于子串的第一个位置不需要相同,所以我们求完 \(LCP\) 后,+1再输出答案。但是,如果 \(LCP\) 是从某一个序列的第一个位置开始的呢?+1肯定就不行了,对于这个序列,前面已经没有东西了。所以还要维护个东西,看看当前区间是否存在后缀开头于某个序列的第一个位置的情况(当年的数据似乎没有卡这个东西,但这个很容易Hack的啊)
这东西我写的太乱了,虽然过了,但是可能还有细节没考虑到,如果大家发现有问题,欢迎提出
#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=1000+10,MAXM=100+10,MAXL=1000000+10,inf=2000;
int cn,n,m,M[MAXN][MAXM],s[MAXL],cnt[MAXL],rk[MAXL],nxt[MAXL],SA[MAXL],height[MAXL],st[MAXN],ed[MAXN],bel[MAXL],nt[MAXN],f[MAXL][22],lg2[MAXL],ans,sum,beg[MAXL],mark,snt[MAXN];
template<typename T> inline void read(T &x)
{
T data=0,w=1;
char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void GetSA()
{
m=(inf<<1);
for(register int i=1;i<=n;++i)rk[i]=s[i];
for(register int i=1;i<=n;++i)cnt[rk[i]]++;
for(register int i=1;i<=m;++i)cnt[i]+=cnt[i-1];
for(register int i=n;i>=1;--i)SA[cnt[rk[i]]--]=i;
for(register int k=1,ps;k<=n;k<<=1)
{
ps=0;
for(register int i=n-k+1;i<=n;++i)nxt[++ps]=i;
for(register int i=1;i<=n;++i)
if(SA[i]>k)nxt[++ps]=SA[i]-k;
for(register int i=1;i<=m;++i)cnt[i]=0;
for(register int i=1;i<=n;++i)cnt[rk[i]]++;
for(register int i=1;i<=m;++i)cnt[i]+=cnt[i-1];
for(register int i=n;i>=1;--i)SA[cnt[rk[nxt[i]]]--]=nxt[i];
std::swap(rk,nxt);
rk[SA[1]]=1;ps=1;
for(register int i=2;i<=n;rk[SA[i]]=ps,++i)
if(nxt[SA[i]]!=nxt[SA[i-1]]||nxt[SA[i]+k]!=nxt[SA[i-1]+k])ps++;
if(ps>=n)break;
m=ps;
}
for(register int i=1,j,k=0;i<=n;height[rk[i++]]=k)
for(k=k?k-1:k,j=SA[rk[i]-1];s[i+k]==s[j+k];++k);
}
inline void add(int x)
{
if(!bel[x])return ;
int las=(snt[bel[x]]==nt[bel[x]]);
if(beg[SA[x]])snt[bel[x]]++;
if((nt[bel[x]]++)==0)sum++;
if(!las&&snt[bel[x]]==nt[bel[x]])mark++;
else if(las&&snt[bel[x]]!=nt[bel[x]])mark=max(mark-1,0);
}
inline void del(int x)
{
if(!bel[x])return ;
int las=(snt[bel[x]]==nt[bel[x]]);
if(beg[SA[x]])snt[bel[x]]--;
if((--nt[bel[x]])==0)sum--;
if(!las&&snt[bel[x]]==nt[bel[x]])mark++;
else if(las&&snt[bel[x]]!=nt[bel[x]])mark=max(mark-1,0);
}
inline void init()
{
for(register int i=1;i<=n;++i)lg2[i]=log(i)/log(2);
for(register int i=1;i<=n;++i)f[i][0]=height[i];
for(register int j=1;(1<<j)<=n;++j)
for(register int i=1;i+(1<<j)-1<=n;++i)f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
inline int GetMn(int l,int r)
{
int k=lg2[r-l+1];
return min(f[l][k],f[r-(1<<k)+1][k]);
}
inline void solve()
{
for(register int i=1;i<=n;++i)
for(register int j=1;j<=cn;++j)
if(st[j]<=SA[i]&&SA[i]<=ed[j])
{
bel[i]=j;
break;
}
init();
for(register int r=1,l=1,now;r<=n;++r)
{
add(r);
while(sum>=cn)del(l++);
if(l>1)add(--l);
if(sum>=cn)chkmax(ans,mark?GetMn(l+1,r):GetMn(l+1,r)+1);
}
}
int main()
{
read(cn);
for(register int i=1,len;i<=cn;++i)
{
read(len);
st[i]=n+1;beg[st[i]]=1;
for(register int j=1;j<=len;++j)read(M[i][j]);
for(register int j=1;j<=len;++j)s[++n]=M[i][j]-M[i][j-1];
ed[i]=n;
s[++n]=inf+i;
}
int nd=0;
for(register int i=1;i<=n;++i)chkmin(nd,s[i]);
nd=-nd+1;
for(register int i=1;i<=n;++i)s[i]+=nd;
GetSA();
solve();
write(ans,'\n');
return 0;
}
【刷题】BZOJ 4698 Sdoi2008 Sandy的卡片的更多相关文章
- BZOJ 4698: Sdoi2008 Sandy的卡片
4698: Sdoi2008 Sandy的卡片 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 106 Solved: 40[Submit][Stat ...
- BZOJ 4698: Sdoi2008 Sandy的卡片 [后缀自动机]
4698: Sdoi2008 Sandy的卡片 题意:差分后就是多个串LCS SAM+map大法好 模板打错 智力-2 #include <iostream> #include <c ...
- BZOJ 4698: Sdoi2008 Sandy的卡片 后缀数组 + RMQ + 查分
题目描述 Sandy和Sue的热衷于收集干脆面中的卡片. 然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型. 每一张卡片都由一些数字进行标记,第i张卡片的 ...
- ●BZOJ 4698 Sdoi2008 Sandy的卡片
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4698 题解: 后缀数组,二分这个题还是比较套路的.首先依据题意,把各个串差分以后,用分割符号 ...
- bzoj 4698: Sdoi2008 Sandy的卡片【SAM】
差分之后用SAM求LCS,然后答案就是LCS+1 #include<iostream> #include<cstdio> #include<cstring> usi ...
- BZOJ 4698: Sdoi2008 Sandy的卡片(后缀数组+差分+二分答案)
传送门 解题思路 看到一个子串加一个数字到另一个子串,自然可以想到差分.然后要把所有串都拼起来,求出\(height\)数组后可以二分答案来做,每次二分一个答案后统计一下连续的\(height> ...
- 4698. [SDOI2008]Sandy的卡片【后缀数组】
Description Sandy和Sue的热衷于收集干脆面中的卡片.然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积 攒卡片兑换超炫的人物模型.每一张卡片都由一些数字进行标记, ...
- 4698: Sdoi2008 Sandy的卡片
前言 总之这个东西说起来很麻烦就是了, 思路 差分合并+后缀数组+二分(dddl) 类似于那个bzoj1031的复制子串和那个poj1743的差分 来看个例子 3 5 1 2 3 4 5 4 1 1 ...
- 【BZOJ4698】[SDOI2008]Sandy的卡片
[BZOJ4698][SDOI2008]Sandy的卡片 题面 flag倒了. bzoj 洛谷 题解 首先题目的区间加很丑对吧, 将每个串差分一下,就可以转化为 求: 给定\(N\)个串,求他们的最长 ...
随机推荐
- spring data jap操作
package com.example.demo; import com.example.entity.UserJ; import com.example.respository.UserJRespo ...
- 一 Hive安装及初体验
一 .Hive安装及初体验 1 .hive简介 Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能. 1.1直接使用hadoop面临的问题 ...
- 在ubuntu安装python, theano, keras , Spearmint, Mongodb
系统配置: Ubuntu 14 (其他系统也差不多如下操作) 1. 通过anaconda安装 python 地址: https://www.continuum.io/downloads#linux 2 ...
- 180531-Spring中JavaConfig知识小结
原文链接:Spring中JavaConfig知识小结/ Sring中JavaConfig使用姿势 去掉xml的配置方式,改成用Java来配置,最常见的就是将xml中的 bean定义, scanner包 ...
- Objective-C 点语法 成员变量的作用域 @property和@synthesize关键字 id类型
点语法 1.利用点语法替换set方法和get方法 方法调用 Student *stu = [Student new]; [stu setAge : 18]; int age = [stu age]; ...
- 【转】APP推广什么是cpa,cps,cpm
转载自:http://www.apptg.cn 经常做做APP推广和做运营的同学对于cpa,cps,cpm,cpc这些名词肯定不会陌生,也基本都知道其表示的含义,但是对于新手来说,这几个词的含义还是不 ...
- UniMelb Comp30022 IT Project (Capstone) - 1.Android入门
1. Android入门 Android系统架构 Android系统:四层架构.五块区域 1. Linux内核层 Linux Kernel:为Android设备的硬件提供了底层驱动 2. 系统运行库层 ...
- leetcode-打家劫舍(动态规划)
你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警. 给定一个代表每 ...
- Java学习 · 初识 IO流
IO流 1. 原理与概念 a) 流 i. 流动,流向 ii. 从一端移动到另一端 源头到目的地 iii. 抽象.动态概念,是一连 ...
- OpenMPI 集群配置
现在有2台机器,希望可以尝试一下在多台机器上跑MPI的感觉,所以跑之前就得配置,先参考网址: https://www.cnblogs.com/awy-blog/p/3402949.html: 1. 配 ...