bzoj 4310: 跳蚤

Description

很久很久以前,森林里住着一群跳蚤。一天,跳蚤国王得到了一个神秘的字符串,它想进行研究。

首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k 个子串中选择字典序最大的那一个。他称其为“魔力串”。

现在他想找一个最优的分法让“魔力串”字典序最小。

Input

第一行一个整数 k。

接下来一个长度不超过 105 的字符串 S。

Output

输出一行,表示字典序最小的“魔力串”。

Sample Input

13

bcbcbacbbbbbabbacbcbacbbababaabbbaabacacbbbccaccbcaabcacbacbcabaacbccbbcbcbacccbcccbbcaacabacaaaaaba

Sample Output

cbc

HINT

S的长度<=100000

这应该是目前见过的最鬼的一道后缀数组题了。。。

最大值最小,考虑二分答案。一开始把子串的排名和第k小的子串求出来了,但是并不知道如何check;

最初的想法是从rnk[1]开始,当前的后缀如果有本质不同的子串排名>mid,就从那个>mid的点为后缀的开头重分一组。

但这样萎得稀巴烂,因为首先这样并不能保证这些子串的子串的排名<=mid,而且这样的贪心也没有正确性。

考虑从sa数组从后往前贪心,每次往前移的时候要把a[i..last]和排名为mid的子串比较一下字典序,如果大于就重分一组,比较子串的话字典序可以找这两个子串的lcp来实现;

这样为什么就保证了子串的子串的排名<=mid呢?因为以i开头的后缀,长度越长字典序越大,所以a[i..last]是以i开头的子串的字典序最大值,最大值都<=mid,其余的子串肯定也都满足。。。

(i为当前扩展的节点,last为这个子串的最后一个元素)

最后判断分的组数是否超过k;

至于本质不同的子串的排名是经典板子,不做赘述,每次打一个新的后缀数组题就感觉以前打的一些东西是错的。。。

是不是求LCP的时候要特判(l==r) ???

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define RG register
#define ll long long
using namespace std;
const int N=1e6+10;
struct data{
    int fir,sec,id;
}x[N];
int sa[N],y[N],rnk[N],rk,height[N],len,k,lx,rx,pre[N],pre2[N],ST[N][20];
ll sum[N];
char a[N];
bool cmp(const data &a,const data &b){
    if(a.fir==b.fir) return a.sec<b.sec;
    else return a.fir<b.fir;
}
void work2(){
    rk=1;y[x[1].id]=rk;
    for(RG int i=2;i<=len;i++){
        if(x[i-1].fir!=x[i].fir||x[i-1].sec!=x[i].sec) rk++;
        y[x[i].id]=rk;
    }
}
void work(){
    sort(x+1,x+1+len,cmp);work2();
    for(RG int i=1;i<=len;i<<=1){
        for(RG int j=1;j+i<=len;j++) x[j].fir=y[j],x[j].sec=y[j+i],x[j].id=j;
        for(RG int j=len-i+1;j<=len;j++) x[j].fir=y[j],x[j].sec=0,x[j].id=j;
        sort(x+1,x+1+len,cmp);work2();
        if(rk==len) break;
    }
    for(int i=1;i<=len;i++) sa[y[i]]=i;
}
void get_height(){
    int kk=0;for(RG int i=1;i<=len;i++) rnk[sa[i]]=i;
    for(RG int i=1;i<=len;i++){
        if(kk) kk--;
        int j=sa[rnk[i]-1];
        while(a[i+kk]==a[j+kk]) kk++;
        height[rnk[i]]=kk;
    }
}
void make_ST(){
  pre[0]=1;for(int i=1;i<=16;i++) pre[i]=pre[i-1]<<1;
  pre2[0]=-1;for(int i=1;i<=len;i++) pre2[i]=pre2[i>>1]+1;
  for(RG int i=2;i<=len;i++) ST[i][0]=height[i];
  for(RG int j=1;j<=16;j++)
    for(RG int i=2;i<=len;i++){
      if(i+pre[j]-1<=len){
        ST[i][j]=min(ST[i][j-1],ST[i+pre[j-1]][j-1]);
      }
    }
}
int query(int l,int r){
  if(l>r) swap(l,r);
  int x=pre2[r-l+1];
  return min(ST[l][x],ST[r-pre[x]+1][x]);
}
int LCP(int l,int r){
  if(l==r) return len-sa[l];
  if(l>r) swap(l,r);
  return query(l+1,r);
}
bool compare(int l1,int r1,int l2,int r2){
    int len1=r1-l1+1,len2=r2-l2+1,lcp=LCP(rnk[l1],rnk[l2]);
    lcp=min(lcp,min(len1,len2));
    if(lcp!=len1&&lcp!=len2) return a[l1+lcp]<=a[l2+lcp];
    if(lcp==len1) return 1;
    if(lcp==len2) return 0;
}
void get_kth(ll kk){
    for(RG int i=1;i<=len;i++){
        if(sum[i]>=kk){
            lx=sa[i];rx=sa[i]+height[i]-1+(kk-sum[i-1]);
            break;
        }
    }
}
bool check(ll mid){
    get_kth(mid);int last=len,ret=1;
    for(RG int i=len;i>=1;i--){
        if(!compare(i,last,lx,rx)){ret++,last=i;}
        if(ret>k) return 0;
    }
    return 1;
}
int main(){
    cin>>k;scanf("%s",a+1);len=strlen(a+1);
    for(RG int i=1;i<=len;i++) x[i].id=i,x[i].fir=x[i].sec=a[i]-‘a‘+1;
    work();get_height();
    for(int i=1;i<=len;i++){sum[i]=sum[i-1]+len-sa[i]+1-height[i];}
    ll L=1,R=sum[len],ans;make_ST();
    while(L<=R){
        ll mid=(L+R)>>1;
        if(check(mid)) ans=mid,R=mid-1;
        else L=mid+1;
    }
    get_kth(ans);
    for(int i=lx;i<=rx;i++) cout<<a[i];
}

  

时间: 2024-10-14 06:48:21

bzoj 4310: 跳蚤的相关文章

后缀数组 hash求LCP BZOJ 4310: 跳蚤

后缀数组的题博客里没放进去过..所以挖了一题写写 充实下博客 顺便留作板子.. 一个字符串S中 内容不同的子串 有 sigma{n-sa[i]+1-h[i]}   (噢 这里的h[]就是大家熟知的height[]) 所以l=1,r=上述sigma 二分 答案是字典序第几大的子串. 然后 求S中第k大的子串W : 因为h[i]是与i-1有关的 所以要从n downto 1,k-=n-sa[i]+1-h[i] 至 k再减就非正了 显然这样扫过来 子串字典序是递减的  因此可以得到第k大子串W 然后再

bzoj 1220 跳蚤

Written with StackEdit. Description \(Z\)城市居住着很多只跳蚤.在\(Z\)城市周六生活频道有一个娱乐节目.一只跳蚤将被请上一个高空钢丝的正中央.钢丝很长,可以看作是无限长.节目主持人会给该跳蚤发一张卡片.卡片上写有\(N+1\)个自然数.其中最后一个是\(M\),而前\(N\)个数都不超过\(M\),卡片上允许有相同的数字.跳蚤每次可以从卡片上任意选择一个自然数\(S\),然后向左,或向右跳\(S\)个单位长度.而他最终的任务是跳到距离他左边一个单位长度

后缀数组小结?

做了一圈(就那么几道还叫一圈)$SA$的题,小结一下,方便自己看 [NOI2016]优秀的拆分 1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 #define mem(x) memset((x),0,sizeof((x))) 6 struct SA{ 7 char s[60005]; 8 int n,m; 9 int t1[60005],t2[600

洛谷 P2827 BZOJ 4721 UOJ #264 蚯蚓

题目描述 本题中,我们将用符号表示对c向下取整,例如:. 蛐蛐国最近蚯蚓成灾了!隔壁跳蚤国的跳蚤也拿蚯蚓们没办法,蛐蛐国王只好去请神刀手来帮他们消灭蚯蚓. 蛐蛐国里现在共有n只蚯蚓(n为正整数).每只蚯蚓拥有长度,我们设第i只蚯蚓的长度为,并保证所有的长度都是非负整数(即:可能存在长度为0的蚯蚓). 每一秒,神刀手会在所有的蚯蚓中,准确地找到最长的那一只(如有多个则任选一个)将其切成两半.神刀手切开蚯蚓的位置由常数p(是满足0<p<1的有理数)决定,设这只蚯蚓长度为x,神刀手会将其切成两只长度

洛谷 P2144 BZOJ 1003 [FJOI2007]轮状病毒

题目描述 轮状病毒有很多变种.许多轮状病毒都是由一个轮状基产生.一个n轮状基由圆环上n个不同的基原子和圆心的一个核原子构成.2个原子之间的边表示这2个原子之间的信息通道,如图1. n轮状病毒的产生规律是在n轮状基中删除若干边,使各原子之间有唯一一条信息通道.例如,共有16个不同的3轮状病毒,入图2所示. 给定n(N<=100),编程计算有多少个不同的n轮状病毒. 输入输出格式 输入格式: 第一行有1个正整数n. 输出格式: 将编程计算出的不同的n轮状病毒数输出 输入输出样例 输入样例#1: 3

BZOJ 1013: [JSOI2008]球形空间产生器sphere

二次联通门 : BZOJ 1013: [JSOI2008]球形空间产生器sphere /* BZOJ 1013: [JSOI2008]球形空间产生器sphere 高斯消元 QAQ SB的我也能终于能秒题了啊 设球心的坐标为(x,y,z...) 那么就可以列n+1个方程,化化式子高斯消元即可 */ #include <cstdio> #include <iostream> #include <cstring> #define rg register #define Max

bzoj 3309 DZY Loves Math - 莫比乌斯反演 - 线性筛

对于正整数n,定义f(n)为n所含质因子的最大幂指数.例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0. 给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b). Input 第一行一个数T,表示询问数. 接下来T行,每行两个数a,b,表示一个询问. Output 对于每一个询问,输出一行一个非负整数作为回答. Sample Input 4 7558588 9653114 6514903 445

【BZOJ】[HNOI2009]有趣的数列

[算法]Catalan数 [题解] 学了卡特兰数就会啦>_<! 因为奇偶各自递增,所以确定了奇偶各自的数字后排列唯一. 那么就是给2n个数分奇偶了,是不是有点像入栈出栈序呢. 将做偶数标为-1,做奇数标为+1,显然当偶数多于奇数时不合法,因为它压不住后面的奇数. 然后其实这种题目,打表就可知啦--QAQ 然后问题就是求1/(n+1)*C(2n,n)%p了,p不一定是素数. 参考bzoj礼物的解法. 看到网上清一色的素数筛+分解质因数解法,不解了好久,感觉写了假的礼物-- 后来觉得礼物的做法才比

洛谷 P2709 BZOJ 3781 小B的询问

题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 输入输出格式 输入格式: 第一行,三个整数N.M.K. 第二行,N个整数,表示小B的序列. 接下来的M行,每行两个整数L.R. 输出格式: M行,每行一个整数,其中第i行的整数表示第i个询问的答案. 输入输出样例 输入样例#1: 6 4 3 1 3 2 1 1 3