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;
}

转载于:https://www.cnblogs.com/hongyj/p/9095240.html

【刷题】BZOJ 4698 Sdoi2008 Sandy的卡片相关推荐

  1. 4698: Sdoi2008 Sandy的卡片

    前言 总之这个东西说起来很麻烦就是了, 思路 差分合并+后缀数组+二分(dddl) 类似于那个bzoj1031的复制子串和那个poj1743的差分 来看个例子 3 5 1 2 3 4 5 4 1 1 ...

  2. 洛谷 P2463 [SDOI2008]Sandy的卡片 解题报告

    P2463 [SDOI2008]Sandy的卡片 题意 给\(n(\le 1000)\)串,定义两个串相等为"长度相同,且一个串每个数加某个数与另一个串完全相同",求所有串的最长公 ...

  3. 【bzoj4698】[Sdoi2008] Sandy的卡片 后缀数组

    题目描述 Sandy和Sue的热衷于收集干脆面中的卡片.然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型.每一张卡片都由一些数字进行标记,第i张卡片的序列 ...

  4. [SDOI2008]Sandy的卡片

    题目描述 Sandy和Sue的热衷于收集干脆面中的卡片. 然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积攒卡片兑换超炫的人物模型. 每一张卡片都由一些数字进行标记,第i张卡片的 ...

  5. Luogu P2463 [SDOI2008]Sandy的卡片

    题目链接 \(Click\) \(Here\) 真的好麻烦啊..事实证明,理解是理解,一定要认认真真把板子打牢,不然调锅的时候真的会很痛苦..(最好是八分钟能无脑把\(SA\)码对的程度\(QAQ\) ...

  6. 洛谷P2463 [SDOI2008]Sandy的卡片(后缀数组SA + 差分 + 二分答案)

    题目链接:https://www.luogu.org/problem/P2463 [题意] 求出N个串中都出现的相同子串的最长长度,相同子串的定义如题:所有元素加上一个数变成另一个,则这两个串相同,可 ...

  7. BZOJ4698: Sdoi2008 Sandy的卡片

    题解: 裸后缀数组+二分答案 /**************************************************************Problem: 4698User: c20 ...

  8. [BZOJ4698][SDOI2008]Sandy的卡片(后缀自动机)

    差分之后就是求多串LCS. 对其中一个串建SAM,然后把其它串放在上面跑. 对SAM上的每个状态都用f[x]记录这个状态与当前串的最长匹配长度,res[x]是对每次的f[x]取最小值.答案就是res[ ...

  9. [luoguP2463] [SDOI2008]Sandy的卡片(后缀数组 + st表)

    传送门 很容易想到,题目中的相同是指差分数组相同. 那么可以把差分数组连起来,中间加上一个没有出现过的且字典序小的数 双指针移动,用st表维护height数组中的最小值. 当然用单调队列应该也可以且更 ...

  10. 洛咕 P2463 [SDOI2008]Sandy的卡片

    哈希水过. 首先这是一段delta相同的序列,按照套路差分一下,b[i]=a[i]-a[i-1],然后就是这些序列的最长公共子段 由于数据范围很小,就可以二分,枚举第一个序列的子段然后每个子序列暴力c ...

最新文章

  1. ubuntu server 10.04 NFS 添加共享目录
  2. JavaScript实现MergeSort归并排序算法(附完整源码)
  3. 关于面试,关于大学——大四狗的自述
  4. 运筹系列77:开源线性规划软件clp使用简介
  5. Arduino开发遥控小车(二)基于nRF24L01无线模块实现数据发送和接收
  6. Icode编程>>>图形化编程>>>1级训练场>>>基础训练【1】
  7. html回调函数,JS 回调函数
  8. 英语的句号在c语言中是什么意思,英语标点符号的用法
  9. 小程序京东首页底部导航栏代码
  10. EOS区块链技术开发(一)环境搭建
  11. Metropolis Method Condition Derivation
  12. Subset sum problem
  13. UG\NX二次开发 菜鸟欢乐多
  14. java.sql.SQLException: 无效的列类型
  15. OPEN.V.xx.N
  16. lmproperly specified vM option.To fix the proble Android studio JVM启动错误
  17. 【原创】无线路由器作为信号放大器设置方法
  18. 【Redis】错误:failed: Hostname must not be empty or null
  19. 建立您的初创企业:通过URL邀请他人
  20. 给手机安装NES模拟器,游戏无限玩

热门文章

  1. 基于Robei:环境光传感器实验设计(及L298N模块控制LED灯板)
  2. 关于深度学习(deep learning)
  3. 点餐app的初步总结
  4. 有关什么情况下session会失效
  5. matlab randn state 2,matlab中randn(‘state’)
  6. 蓝牙通信的简要设计与开发(附加题)
  7. 扫雷算法实现(简易版,只能在CMD指令中玩)
  8. 盘点百度 4 个牛逼哄哄的开源项目
  9. 【Linux】 基础入门 全解
  10. ROS学习之工作空间与创建过程