[arc077f]SS

前言

看错题,这题我做了2.5h…

题目大意

给你一个形如ss的小写字母字符串,即两个一样的字符串拼接。
然后定义一个函数f,f(ss)表示一个字符串,他形如ss+t,然后f(ss)也要能够写成s1s1的形式。
问调用f 10^100次后,得到的字符串的s[l..r]中,a..z各出现多少次。
l,r<=1e18,|s|<2e5

解题思路

首先打表找一下规律…发现每次调用,要么字符串增加一段相等的东西,要不就像斐波那契数列一样增加。
我们来分析一下调用一次会发生什么。
考虑SS=s[1]…s[n]s[1]…s[n]。那么我们肯定要在后面增加2k个字符,那么变成s[1]…s[n]s[1]…[k] s[k+1]…s[n]T[1]…T[2k],其中T是新加入的字符。先不管T是什么,我们可以直接发现s有个k的周期。那么后面的T你直接对应一下即可。
那么我们发现f(SS)=STST,其中T为s[1..k],k为s最短周期。
这样我们直接分析一个S即可。设g(s)=st,也就是f的一半。
根据我们打表,加一点分析:
1,若一开始k整除|S|,那么 g+(s)=stttttt... g + ∞ ( s ) = s t t t t t t . . .
2,其他情况,g(s)=st,g(st)=sts… gi(s)=gi1(s)+gi2(s) g i ( s ) = g i − 1 ( s ) + g i − 2 ( s )
那么我们通过这个可以直接计算次数了。因为第二种情况不用100次长度就爆掉1e18了。
注意到1和2的情况可以一起搞,由于长度很大我们直接用g跑。一开始用f跑不知道干嘛错了。
怎么证明这两个性质呢?我们证明2就好,1的话很好搞。
设s=t*n+t’,其中t’是t的一个前缀且t’≠t,t是s的最短周期。我们想要证明g(s)=t*n+t’+t的最短周期是|t|*n+|t’|。
假设存在周期x<|t|*n+|t’|。
情况1:x mod |t|≠ |t’|,那么t必然有另一个周期gcd(|T|,x-|T|),画画就知道,那么S也有这个周期,那么t就不是s的最短周期。矛盾
情况2: x mod |t|=|t’|,这种情况也类似。

代码

#include<cstdio> 
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
typedef long long ll;
const int N=2e6+5,M=1e7+50,mo=1e9+7;
int fail[N],n,i,per;
ll len1,len2,d,prt[30],l,r,f[305][26],g[305][26],sf[305],sg[305];
char s[N];
void kmp()
{
    int i,j;
    j=0;
    n=strlen(s+1)/2;
    fo(i,2,n)
    {
        while (j&&s[i]!=s[j+1]) j=fail[j];
        if (s[i]==s[j+1]) j++;
        fail[i]=j;
    }
}
void fib()
{
    int i,j;
    fo(i,2,300)
    {
        fo(j,0,25)  g[i][j]=g[i-1][j]+g[i-2][j];
        sg[i]=sg[i-1]+sg[i-2];
        if (sg[i]*2>r) break;
    }
    fo(i,0,300)
    {
        fo(j,0,25) f[i][j]=g[i][j]*2;
        sf[i]=sg[i]*2;
        if (sf[i]>r) break;
    }
}
void calc(ll x,ll xs)
{
    int pos;
    fo(pos,0,300)
        if (sg[pos]>x) break;
    while (pos>=0)
    {
        if (sg[pos]<=x)
        {
            fo(i,0,25) prt[i]+=xs*g[pos][i];
            x-=sg[pos];
            pos--;
        }else
        pos--;
    }
    fo(i,1,x) prt[s[i]-'a']+=xs; 
}
int main()
{
    freopen("t20.in","r",stdin);
    freopen("t20.out","w",stdout);
    scanf("%s %lld %lld",s+1,&l,&r);
    kmp();
    fo(i,1,n) g[0][s[i]-'a']++,sg[0]++;
    per=n-fail[n];
    fo(i,0,25) g[1][i]=g[0][i];
    sg[1]=sg[0];
    fo(i,1,per) g[1][s[i]-'a']++,sg[1]++;
    fib();
    calc(r,1);
    calc(l-1,-1);
    fo(i,0,25) printf("%lld ",prt[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值