p5341 [TJOI2019]甲苯先生和大中锋的字符串

分析

TJOI白给题

建出sam,对于每个点如果它的子树siz和等于k

那么对于这个满足的点它有贡献的长度一定是一个连续区间

直接差分即可

代码

#include<bits/stdc++.h>
using namespace std;
int n,k,mx,ans,d[100100];
char s[100100];
struct SAM {
    int mp[200100][30],fa[200100],ed,ccnt,len[200100],siz[200100];
    int head[200100],nxt[200100],to[200100],cnt;
    inline void init(){
      mx=0;
      cnt=ans=0;
      ccnt=ed=1;
      memset(d,0,sizeof(d));
      memset(mp,0,sizeof(mp));
      memset(fa,0,sizeof(fa));
      memset(to,0,sizeof(to));
      memset(len,0,sizeof(len));
      memset(siz,0,sizeof(siz));
      memset(nxt,0,sizeof(nxt));
      memset(head,0,sizeof(head));
    }
    inline void add(int x,int y){
      nxt[++cnt]=head[x];
      head[x]=cnt;
      to[cnt]=y;
    }
    inline void ins(int x,int n1){
        int p=ed;
        ed=++ccnt;
        siz[ccnt]=1;
        len[ccnt]=n1;
        while(p&&!mp[p][x]){
          mp[p][x]=ed;
          p=fa[p];
        }
        if(!p){
          fa[ed]=1;
          return;
        }
        int q=mp[p][x];
        if(len[q]==len[p]+1){
          fa[ed]=q;
          return;
        }
        len[++ccnt]=len[p]+1;
        for(int i=1;i<=26;i++)mp[ccnt][i]=mp[q][i];
        fa[ccnt]=fa[q];
        fa[q]=ccnt;
        fa[ed]=ccnt;
        for(int i=p;mp[i][x]==q;i=fa[i])mp[i][x]=ccnt;
    }
    inline void build(){
      for(int i=2;i<=ccnt;i++)add(fa[i],i);
    }
    inline void dfs(int x){
      for(int i=head[x];i;i=nxt[i])
        dfs(to[i]),siz[x]+=siz[to[i]];
      if(siz[x]==k&&len[fa[x]]+1<=len[x])d[len[fa[x]]+1]++,d[len[x]+1]--;
    }
};
SAM sam;
int main(){
    int i,j,t;
    scanf("%d",&t);
    while(t--){
      sam.init();
      scanf("%s",s+1);
      n=strlen(s+1);
      scanf("%d",&k);
      for(i=1;i<=n;i++)sam.ins(s[i]-‘a‘+1,i);
      sam.build();
      sam.dfs(1);
      for(i=1;i<=n;i++){
          d[i]+=d[i-1];
          if(d[i]&&d[i]>=mx){
            mx=d[i];
            ans=i;
        }
      }
      printf("%d\n",ans?ans:-1);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/yzxverygood/p/11519053.html

时间: 2024-08-30 18:22:19

p5341 [TJOI2019]甲苯先生和大中锋的字符串的相关文章

[TJOI2019]甲苯先生和大中锋的字符串

有个叫asuldb的神仙来嘲讽我 说这题SAM水题,而且SA过不了 然后我就用SA过了 显然是一个Height数组上长为k的滑块,判一下两边,差分一下就可以了 #include"cstdio" #include"cstring" #include"iostream" #include"algorithm" using namespace std; const int MAXN=1e5+5; int n,T,mx,hd,tl;

【TJOI2019】甲苯先生和大中锋的字符串

题目链接:https://www.luogu.com.cn/problem/P5341 题目大意:给定 \(T\) 个字符串, 分别求出这 \(T\) 个字符串中所有恰好出现 \(k\) 次的子串中 , 出现过最多次数的长度的最大值 solution 对每个字符串 , 先求出它的 \(height\) 数组 考虑恰好出现 \(k\) 次的子串 \(s\) , 对于 \(height\) 数组中连续的 \(h[i] ... h[i + k - 1]\) , 其必然满足 \(len(s) < min

【题解】Luogu P5340 [TJOI2019]大中锋的游乐场

原题传送门 没想到省选也会出这种题??! 实际就是一个带有限制的最短路 因为\(k<=10\),所以我们珂以暴力将每个点的权值分为[-k,k],为了方便我们珂以转化成[0,2k],将汉堡的权值记为1,可乐的权值记为-1,最短路即可,如果发现不合理的就果断扔掉即可(不知道有没有好事之徒用SPFA写) #include <bits/stdc++.h> #define N 10005 #define pi pair<int,int> #define getchar nc using

luogu P5338 [TJOI2019]甲苯先生的滚榜

传送门 首先,排名系统,一看就知道是原题,可以上平衡树来维护 然后考虑一种比较朴素的想法,因为我们要知道排名在一个人前面的人数,也就是AC数比他多的人数+AC数一样并且罚时少的人数,所以考虑维护那两个东西.AC数更多的人数显然可以直接上树状数组.后者的话可以对每一种AC数开值域线段树,存每个罚时有多少人,注意到罚时之和不会超过\(1.5*10^6\),所以动态开点线段树可以轻松解决.然后每次有个人AC数和罚时改变就先在原来的位置-1,然后在新位置+1.每次询问就是树状数组上AC数\(>\)当前A

【题解】Luogu P5342 [TJOI2019]甲苯先生的线段树

原题传送门 挺有趣的一道题 \(c=1\),暴力求出点权和n即可 \(c=2\),先像\(c=1\)一样暴力求出点权和n,考虑有多少路径点权和也为n 考虑设x为路径的转折点,\(L\)为\(x\)向左儿子走的长度,\(R\)为\(x\)向右儿子走的长度.易知当\(L,R\)确定时,有唯一的\(x\)对应 以\(x\)为转折点,\(L,R\)为向左/右儿子走的距离,这时点权和至少为\(Min=(2^{L+1}+2^{R+1}-3)x+2^R-1\) 此时x的取值一定珂以求出.考虑一下如何产生剩下\

【题解】Luogu P5337 [TJOI2019]甲苯先生的字符串

原题传送门 我们设计一个\(26*26\)的矩阵\(A\)表示\(a~z\)和\(a~z\)是否能够相邻,这个矩阵珂以由\(s1\)得出.答案显然是矩阵\(A^{len_{s2}-1}\)的所有元素之和,矩阵快速幂即可 #include <bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; inline void write(register int x) { if(!x)putchar

第十一届“蓝狐网络杯”湖南省大学生计算机程序设计竞赛 B - 大还是小? 字符串水题

B - 大还是小? Time Limit:5000MS     Memory Limit:65535KB     64bit IO Format: Description 输入两个实数,判断第一个数大,第二个数大还是一样大.每个数的格式为: [整数部分].[小数部分] 简单起见,整数部分和小数部分都保证非空,且整数部分不会有前导 0.不过,小数部分的最 后可以有 0,因此 0.0 和 0.000 是一样大的. Input 输入包含不超过 20 组数据.每组数据包含一行,有两个实数(格式如前所述)

大数据阶乘——字符串乘法器

char s[1001]; //字符串s存储乘法得到的大数字,s[0]代表低位 int GetLength()//返回s的最大不为空的元素下标 { int i; for(i=1000; i>=0; i--) { if(s[i]!='0') { break; } } return i; } void cal(int k)//计算大数字s和小数字k相乘 { int add=0;//进位数 int i; int temp; for(i=0; i<=GetLength(); i++)// { temp

2015福富福大笔试——实现字符串右移

前两天参加了福富在福大的宣讲会,并且参加了笔试,最后一道大题,这里讲一下当时我的解法,大概的题意是这个样子的,只能使用c的库,实现一个函数void MakeString(char *pStr,int n)(ps:这里的函数名是我现在取的,想不起来考题给的是什么了),函数要求是以'\0'结尾的字符串pStr,一个需要右移的字符个数n,实现类似输入这样MakeString("abcdefghi",2),字符串右移后变成:hiabcdeg: 以下给出我当时的解法,希望各位看官有什么意见或者建