首页 > 其他分享 >P3975 [TJOI2015] 弦论 题解

P3975 [TJOI2015] 弦论 题解

时间:2023-06-30 15:33:16浏览次数:45  
标签:26 return P3975 int 题解 弦论 -- 字串 sa

一、题目描述:

  给你一个长度为 $n$ 的字符串,字符串由 $26$ 个小写字母组成,求第 $k$ 大的字串。

    给定参数 $t$ :

    $t=0:\ 位置不同的相同字串只算一个。$

    $t=1:\ 位置不同的相同字串算作多个。$

  若字串数量不足 $k$ 个,输出 $-1$ 。

  数据范围:$1\le n\le 5\times 10^5,1\le k\le 1\times 10^9$ 。


 二、解题思路:

  后缀数组求出 $sa$ 和 $height$ 数组。

  $t=0:$ 经典问题 $=>$ 不同字串个数,时间复杂度 $O(nlog_2^n)$ 。

  $t=1:$ 比较复杂,细说。

    考虑与 $t=0$ 时的区别,当前这这一位的字母多次出现,要算多次。

    所以二分出这个字母的右边界,如果字串数量不够就跳下一个字母,否则进入下一位的判断。

    由于字符串由 $26$ 个小写字母组成,所以每一轮二分次数不超过 $26$ 次,时间复杂度 $O(26\times nlog_2^n)$ 。


 三、完整代码:

  1 #include<iostream>
  2 #define N 1000010
  3 using namespace std;
  4 string s;
  5 int n,m,t,k,q=122,L,R[N];
  6 int p[N];
  7 int h[N],rak[N],sa[N],tp[N];
  8 struct ST{
  9     int l,r,id;
 10     bool operator != (const ST &t) const {
 11         if(t.l!=l) return 1;
 12         if(t.r!=r) return 1;
 13         return 0;
 14     }
 15 }a[N],b[N];
 16 void qsort()
 17 {
 18     for(int i=0;i<=m;i++) tp[i]=0;
 19     for(int i=1;i<=n;i++) tp[a[i].r]++;
 20     for(int i=1;i<=m;i++) tp[i]+=tp[i-1];
 21     for(int i=n;i>=1;i--) b[tp[a[i].r]--]=a[i];
 22     for(int i=1;i<=m;i++) tp[i]=0;
 23     for(int i=1;i<=n;i++) tp[b[i].l]++;
 24     for(int i=1;i<=m;i++) tp[i]+=tp[i-1];
 25     for(int i=n;i>=1;i--) a[tp[b[i].l]--]=b[i];
 26 }
 27 void suffixsort()
 28 {
 29     for(int i=1;i<=n;i++)
 30         rak[i]=s[i-1];
 31     for(int w=1;w<=n;w<<=1)
 32     {
 33         for(int i=1;i<=n;i++)
 34             a[i].l=rak[i],a[i].r=rak[i+w],a[i].id=i;
 35         m=q;q=0;qsort();
 36         for(int i=1;i<=n;i++)
 37             rak[a[i].id]=(q+=a[i]!=a[i-1]);
 38         if(q==n) break;
 39     }
 40     for(int i=1;i<=n;i++)
 41         sa[rak[i]]=i;
 42 }
 43 void get_height()
 44 {
 45     int k=-1;
 46     for(int i=1;i<=n;i++)
 47     {
 48         if(k>=0) k--;
 49         int j=sa[rak[i]-1];
 50         while(s[i+k]==s[j+k]) k++;
 51         h[rak[i]]=k+1;
 52     }
 53 }
 54 void pre_work()
 55 {
 56     L=1;R[0]=n;
 57     for(int i=1;i<=n;i++)
 58         p[i]=p[i-1]+n-sa[i]+1;
 59 }
 60 bool check(int x,char val,int pos)
 61 {
 62     int tmp=sa[x]+pos-2;
 63     if(tmp>=n)return 0;
 64     return s[tmp]==val;
 65 }
 66 int Find(int ll,int rr,int x)
 67 {
 68     char val=s[sa[ll]+x-2];int ans;
 69     while(ll<=rr)
 70     {
 71         int mid=(ll+rr)>>1;
 72         if(check(mid,val,x)){
 73             ans=mid;
 74             ll=mid+1;
 75         }else rr=mid-1;
 76     }
 77     return ans;
 78 }
 79 int main()
 80 {
 81     ios::sync_with_stdio(false);
 82     cin.tie(0);cout.tie(0);
 83     cin>>s>>t>>k;n=s.length();
 84     suffixsort();get_height();
 85     if(t==0){
 86         for(int i=1;i<=n;i++)
 87             if(k<=n-sa[i]+1-h[i]){
 88                 for(int j=sa[i];j<sa[i]+k+h[i];j++)
 89                     cout<<s[j-1];return 0;
 90             }else k-=n-sa[i]+1-h[i];
 91         cout<<-1<<'\n';
 92     }else{
 93         pre_work();
 94         if(1ll*n*(n+1)/2<k){
 95             cout<<-1<<'\n';
 96             return 0;
 97         }
 98         for(int i=1;i<=n;i++)
 99         {
100             while(sa[L]+i-1>n&&L<n)
101                 L++;R[i]=Find(L,R[i-1],i);
102             int tmp=p[R[i]]-p[L-1]-(R[i]-L+1)*(i-1);
103             while(tmp<k)
104             {
105                 k-=tmp;L=R[i]+1;
106                 R[i]=Find(L,R[i-1],i);
107                 tmp=p[R[i]]-p[L-1]-(R[i]-L+1)*(i-1);
108             }
109             if(L==R[i]){
110                 for(int cc=1;cc<=k;cc++)
111                     cout<<s[sa[L]+i+cc-3];
112                 break;
113             }else    cout<<s[sa[L]+i-2],k-=R[i]-L+1;
114         }
115     }
116     return 0;
117 }

四、写题心得:

  个人觉得是比较版也比较难的 $sa$ 题。细节比较多,但是一遍就过了!收获经验如下:

  $1、sa\ 写的很熟练,值得表扬!=> Exp++!$

  $2、记得乘法的时候检查要不要开\ long\ long\ 。=> Exp++!$

标签:26,return,P3975,int,题解,弦论,--,字串,sa
From: https://www.cnblogs.com/yhy-trh/p/P3975.html

相关文章

  • 【js学习笔记十四】普通函数中的this指向问题解决方案_this
     目录前言导语 解决思路运行结果前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷导语歌谣歌谣......
  • 【js学习笔记十五】普通函数中的this指向问题解决方案箭头函数
     目录前言导语 解决思路运行结果前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷导语歌谣歌谣......
  • Hadoop常见问题解析
    Hadoop常见问题解析Hadoop特性1.高可靠性:采用冗余数据存贮方式,即使一个副本发生故障,其他副本也可以保证对外工作的正常进行。2.高效性:作为并行分布式计算平台,hadoop采用分布式存贮和分布式处理两大核心技术,能够高效的处理PB级别的数据3.高可扩展性:hadoop的设计目标是可以高效......
  • 项目中pom依赖冲突问题解决
    首先,确定插件中jBoss-JBPM和UML已经勾上 然后安装maven-Helper 在pom.xml中,右键点击diagrams,showDependencies 则可以看到各依赖关联情况,其中,红色虚线则表示有依赖冲突 如图中的红色虚线,是插件自己标识的,不是本人后期画的 在pom.xml最下方,可以切换sheet,切换到......
  • 题解 P8757 [蓝桥杯 2021 省 A2] 完美序列
    题解P8757[蓝桥杯2021省A2]完美序列题意如果一个序列是单调递减的,而且除了第一个数以外的任何一个数都是上一个数的因数,则称这个序列为一个完美序列。一个序列中的一个子序列如果是完美序列,则称为该序列的一个完美子序列。一个序列的最长完美子序列长度,称为该序列的完美......
  • DLL-FILES.COM - 您的DLL问题解决方案!--九五小庞
    每个人都遇到过“无法找到****.dll文件...”的消息弹窗。各位,这个问题终于可以解决了!在这里你可以找到电脑上最常丢失或损坏的文件。自由下载,无任何费用! ......
  • AT_arc067_f 题解
    传送门Simplify不难想到其实题意就是让你求:\[\max_{1\lel\ler\len}\left\{\sum_{i=1}^m\max_{l\lej\ler}\{b_{i,j}\}-\sum_{i=l}^ra_i\right\}\]Solution首先考虑暴力,我的话是枚举\(l,r(l\in[1,n],l\in[1,r])\),然后\(m\)个单调队列先把\(l\)到\(r\)的b数组存进......
  • ABC143F 题解
    前言题目传送门!更好的阅读体验?很有趣的题。提供一种和现有题解略微不同的做法。思路突破点在于反着想。当最多能取\(x\)次时,每次我取的不同数字的数量,最多是多少。统计一下每个数字出现的次数\(cnt_i\)。那么这个最多次数应为\[\left\lfloor\dfrac{\sum\min(cnt_i,x)}x......
  • 「ARC133E」Cyclic Medians 题解
    本文网址:https://www.cnblogs.com/zsc985246/p/17513317.html,转载请注明出处。传送门「ARC133E」CyclicMedians题目大意给定\(n,m,V,A\),你需要计算所有长为\(n\)、值域为\([1,V]\)的整数序列\(x\)和长为\(m\)、值域为\([1,V]\)的整数序列\(y\)形成的序列对\((x_......
  • Codeforces[CF1036B]Diagonal Walking v.2题解
    题目大意很明显,这道题就是求k步之内到达点\((a,b)\),然后尽量走对角线,求能走对角线的最大值。做题思路首先明白一个事实,即一个对角线可以通过增加一步而抵达点不变,如图:我们可以这样思考这道题,在到达目的地以后,剩余步数如果为双数,则在对角线来回走,最后会到目的地。但如果剩......