A Horrible Poem

https://loj.ac/problem/10038

题目描述

  给出一个字符串S,以及q次询问,每次询问这个字符串子串的最短循环节。

思路

  这道题是毒瘤题,一定是毒瘤题,至少loj的数据是。我有两个思路,一个93分,一个不加快读97分,加快读满分。

   首先,很显然,对于一个字符串求最小循环节,枚举为其长度因数的前缀。但这样显然会T,我们考虑优化。

   ①我们考虑可以通过预处理,快速求出一段子串内所有字母的个数,而最小循环节中的字母个数一定是所有字母数的公因数,那么最小循环节的个数一定是这个公因数的因数,才有可能是循环节。所以我们枚举[1, ],看是否为循环节,更新答案。最后93分。

   ②第一个做法跑不过去的原因是枚举时候有过多的无用状态,但又难以继续优化,我们再从最初的想法开始。我们想对于最小循环节长度,它的倍数也一定是循环节。我们先用线性筛取出到原字符串长度的所有数的最小质因数。之后每次询问把询问子串的长度进行质因数分解,求出其每一个质因数,再尝试除以每一个质因数,不断缩小len,最后答案就是最小循环节。

代码(方法一,93分)

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const ull p=131,MAXN=5e5+10;
char s[MAXN];
ull sum[MAXN],power[MAXN],ans;
ull f[MAXN][30];
ull f_hash(ull l,ull r)
{
    return (ull)sum[r]-sum[l-1]*power[r-l+1];
}
ull gcd(ull x,ull y)
{
    return y==0?x:gcd(y,x%y);
}
void check(ull l,ull r,ull len)
{
    if(f_hash(l,r-len)==f_hash(l+len,r))
        ans=min(ans,len);
}
ull read()
{
    ull ret=0,w=1;
    char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){ret=ret*10+ch-‘0‘;ch=getchar();}
    return ret*w;
}
int main()
{
    ull n;
    n=read();
    gets(s+1);
    power[0]=1;
    for(ull i=1;i<=n;i++)
    {
        sum[i]=sum[i-1]*p+(s[i]-‘a‘+1);
        power[i]=power[i-1]*p;
    }
    for(ull i=1;i<=n;i++)
    {
        for(ull j=1;j<=26;j++)
            f[i][j]=f[i-1][j];
        f[i][s[i]-‘a‘+1]++;
    }
    ull q;
    q=read();
    while(q--)
    {
        ull l,r;
        l=read();r=read();
        ull len=r-l+1,vgcd=r-l+1;
        for(ull i=1;i<=26;i++)
            vgcd=gcd(vgcd,f[r][i]-f[l-1][i]);
        ans=1e9;
        for(ull i=1;i*i<=vgcd;i++)
            if(vgcd%i==0)
            {
                check(l,r,len/i);
                check(l,r,len/(vgcd/i));
            }
        printf("%llu\n",ans);
    }
    return 0;
}

代码(正解,满分)

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const ull p=47,MAXN=5e5+10;
char s[MAXN];
ull sum[MAXN],power[MAXN],ans,ys[MAXN],prime[MAXN],nxt[MAXN];
bool used[MAXN];
ull f_hash(ull l,ull r)
{
    return (ull)sum[r]-sum[l-1]*power[r-l+1];
}
bool check(ull l,ull r,ull len)
{
    return f_hash(l,r-len)==f_hash(l+len,r);
}

ull read()
{
    ull res=0,w=1;
    char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
    return res*w;
}
void write(ull x)
{
    if(x>9)write(x/10);
    putchar(x%10^48);
}
void writeln(ull x)
{
    write(x);
    putchar(‘\n‘);
}

void pri()
{
    ull k=0;
    for(ull i=2;i<=MAXN;i++)
    {
        if(!used[i])
        {
            prime[++k]=i;
            nxt[i]=i;
        }
        for(int j=1;j<=k&&i*prime[j]<=MAXN;j++)
        {
            ull m=i*prime[j];
            used[m]=1;
            nxt[m]=prime[j];
            if(i%prime[j]==0)break ;
        }
    }
}
int main()
{
    ull n=read();
    scanf(" %s",s+1);
    power[0]=1;
    for(ull i=1;i<=n;i++)
    {
        sum[i]=sum[i-1]*p+(s[i]-‘a‘+1);
        power[i]=power[i-1]*p;
    }
    pri();
    ull q=read();
    while(q--)
    {
        ull l=read(),r=read();
        ull len=r-l+1,cnt=0;
        while(len!=1)
        {
            ys[++cnt]=nxt[len];
            len/=nxt[len];
        }
        len=r-l+1;
        for(int i=1;i<=cnt;i++)
        {
            int tmp=len/ys[i];
            if(check(l,r,tmp))
                len=tmp;
        }
        writeln(len);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/fangbozhen/p/11620725.html

时间: 2024-10-21 10:07:18

A Horrible Poem的相关文章

[BZOJ2795][Poi2012]A Horrible Poem

2795: [Poi2012]A Horrible Poem Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 261  Solved: 150[Submit][Status][Discuss] Description 给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节.如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到. Input 第一行一个正整数n (n<=500,000),表示S的长度.第二行n个小

2795: [Poi2012]A Horrible Poem

2795: [Poi2012]A Horrible Poem Time Limit: 50 Sec  Memory Limit: 128 MBSubmit: 484  Solved: 235[Submit][Status][Discuss] Description 给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节.如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到. Input 第一行一个正整数n (n<=500,000),表示S的长度.第二行n个小

【BZOJ2795】[Poi2012]A Horrible Poem hash

[BZOJ2795][Poi2012]A Horrible Poem Description 给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节.如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到. Input 第一行一个正整数n (n<=500,000),表示S的长度.第二行n个小写英文字母,表示字符串S.第三行一个正整数q (q<=2,000,000),表示询问个数.下面q行每行两个正整数a,b (1<=a<=b<=n),表示询

BZOJ 2795: [Poi2012]A Horrible Poem( hash )

...字符串hash. #include<bits/stdc++.h> using namespace std; typedef unsigned long long ull; const int maxn = 500009; const ull P = 1000173169; char S[maxn]; int check[maxn], prime[maxn], N = 0, n; ull H[maxn], K[maxn]; void init() { memset(check, 0, si

【bzoj2795】[Poi2012]A Horrible Poem Hash+分解质因数

题目描述 给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节.如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到. 输入 第一行一个正整数n (n<=500,000),表示S的长度.第二行n个小写英文字母,表示字符串S.第三行一个正整数q (q<=2,000,000),表示询问个数.下面q行每行两个正整数a,b (1<=a<=b<=n),表示询问字符串S[a..b]的最短循环节长度. 输出 依次输出q行正整数,第i行的正整数对应第i

bzoj-2795 A Horrible Poem

题意: 给出一个长度为N的字符串,有Q次询问: 每次询问给出一个区间,求区间最短循环节长度: N<=500000,Q<=2000000: 题解: 这题数据范围简直丧病= =渣电脑3s真的能跑出来吗... 不过这题在BZ上是可做的,50s我的程序10s出解了: 首先这问题画一画发现它绝对不是什么数据结构能维护的,因为这东西毫无可并性: 硬说的话多个相同的的连在一起的循环节可以合并变长,然并卵: 所以如果考虑暴力一些呢? 发现循环节长度一定是区间长度n的约数,可以枚举n的约数: 验证利用RKhas

【bzoj2795】【Poi2012】A Horrible Poem

题解: 询问区间的整循环节 设区间长度为$n$ 如果有循环节长为$x$和$y$,那由斐蜀定理得$gcd(x,y)$也一定为一个循环节: 假设最小的循环节长为$mn$,那么对于任何循环节长$x$,一定$mn | x$ , 否则$gcd(mn,x)<mn$矛盾 推出$\frac{n}{x} | \frac{n}{mn}$ 所以每次提出$n$的一个质因子$p$,考虑是否可以分成$p$段,如果可以$n=\frac{n}{p}$继续找: 最后得出来的$n$就是最短的循环节: 分解质因子可以$O(n)$线筛

[Poi2012]A Horrible Poem

先放个简单版的 Pku2406 Power Strings Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc" and b = "def" then a*b = "abcdef". If we think of concatenation as multiplication, exponentiation by a

POI2012 (持续更新中)

Distance Well Vouchers Cloakroom A Horrible Poem Rendezvous Fibonacci Representation Squarks