CF1466G-Song of the Sirens

\(\texttt{data-2021-01-03}\)

更正式子错误,同时增加了一些细节。


考场上挂于 F,没看 G,说实话自己会写的题没看到真的非常可惜。

这道题并不算是难,如果说做过 CF177G 这道题的话,基本可以一眼秒。


注:以下的前缀后缀均指保留水手名字的长度位。

前面的思路是和 CF177G 一样的,考虑当 的长度大于等于 水手名字 长度之后,再进行变换不会改变歌的 前缀后缀

这有什么用呢?假设当前歌为 \(S\),水手名字为 \(N\)\(|S|\ge |N|\)),歌的前缀为 \(S_1\),后缀为 \(S_2\),中间新添加的字母为 \(x\)。定义 \(F_S\) 表示 \(S\) 串中 \(N\) 的出现次数,那么有:

\[\begin{cases} F_S\to 2F_{S}+F_{S_2+x+S_1}\\ S\to S+x+S \end{cases} \]

注:字符串加法为拼接。

会发现 \(S_1\)\(S_2\) 不变,每次变换就等于乘二然后加一个和 \(x\) 有关的数值,而因为 \(x\) 只有 \(26\) 种可能,所以可以用正确的复杂度处理出来,可设函数 \(g_x=F_{S_2+x+S_1}\)

然后第一次 长度大于等于 \(|N|\) 的串 \(S\) 的贡献(答案初值) 可以 Hash 暴力计算。

我们现在以这个暴力处理出来的串当做初始的 \(S_0\)(第 \(0\) 首歌),以便之后的式子描述。

那么假设我们现在要求的是第 \(m\) 首歌(新标号)的 \(F\) 值。

然后考虑答案计算的式子,直接暴力拆开,得到的是每一项乘上一个 \(2\) 的幂次的形式:

\[F_{S_0}\cdot 2^{m}+\sum_{i=1}^m g_{t_i}\cdot 2^{m-i} \]

再提取一次公因式,除 答案初值 以外,可以将不同的 \(g_x\) 提取出来,得到这样一个东西:

\[F_{S_0}\cdot 2^{m}+(\sum_{i=a}^z g_{i} \cdot(\sum 2^{k_i})) \]

后面那个东西就是若干个 \(2\) 的幂次的和,因为不太好用式子写,所以简单地表示一下。

后面这个东西依然是可以预处理的,当然预处理的是提取了一个 \(2^{-p},p\in[0,n]\) 次的结果,这部分没理解可以看代码,是运用前缀和的思想,把每首歌的 序号和贡献 塞入 这首歌的中间连接字母

代码的答案计算部分写得比较密集(指一堆函数嵌套),可能观感并不佳,不过这道题思路有了代码 有手就行 自己实现并非难事。

#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>
#include <iostream>
#define LL long long
using namespace std;
inline int rin()
{
    int s=0;
    bool bj=false;
    char c=getchar();
    for(;(c>'9'||c<'0')&&c!='-';c=getchar());
    if(c=='-')bj=true,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())s=(s<<1)+(s<<3)+(c^'0');
    if(bj)s=-s;
    return s;
}

const int N=1e5+3;
const int S=1e6+3;
const int M=1e9+7;

inline int prpr(int x,int y){return 1LL*x*y%M;}
inline int ksm(int x,int y){int ans=1;for(;y;y>>=1){if(y&1)ans=prpr(ans,x);x=prpr(x,x);}return ans;}

int n,q;
string t;
string s[22];
inline void Read()
{
    n=rin();q=rin();
    cin>>s[0];
    cin>>t;
    return;
}

const int Mod=19260817;
const int K=37;

int sl[S<<1];
inline int Hash_add(int x,int y){return (x+y)%Mod;}
inline int Hash_prpr(int x,int y){return 1LL*x*y%Mod;}
inline int Hash_check(int *a,int l,int r){return (l>r)?(0):((a[r]-Hash_prpr(a[l-1],sl[r-l+1])+Mod)%Mod);}

int pro[N];
int inv[N];

struct gyq
{
    vector<int>num;
    vector<int>add;
    inline void push(int num_,int val_)
    {
        num.push_back(num_);
        if(add.empty())add.push_back(val_);
        else add.push_back((add.back()+val_)%M);
        return;
    }
    inline int cheak(int ed)
    {
        int l=0,r=num.size()-1;
        int ans=0;
        for(;l<=r;)
        {
            int mid=(l+r)>>1;
            if(num[mid]<=ed)ans=add[mid],l=mid+1;
            else r=mid-1;
        }
        return ans;
    }
}d[26];
inline void init()
{
    Read();
    for(int i=1;s[i-1].length()<1e6;i++)s[i]=s[i-1]+t[i-1]+s[i-1];

    sl[0]=1;
    for(int i=1;i<(S<<1);i++)sl[i]=Hash_prpr(sl[i-1],K);
    
    pro[0]=inv[0]=1;
    for(int i=1;i<=n;i++)pro[i]=prpr(pro[i-1],2);
    inv[n]=ksm(pro[n],M-2);
    for(int i=n-1;i>=1;i--)inv[i]=prpr(inv[i+1],2);

    for(int i=1;i<=n;i++)d[t[i-1]-'a'].push(i,pro[n-i]);
    return;
}

string Alpha;
int lens;
int num;
int L;

int Beta[S];
int Zeta[S<<1];
int Gamma[26];

LL ans;
inline int work()
{
    memset(Gamma,0,sizeof(Gamma));
    int i,j,k;
    num=rin();
    cin>>Alpha;
    lens=Alpha.length();
    for(i=0;s[i].length()<lens&&i<num;i++);if(s[i].length()<lens)return 0;
    for(j=1;j<=lens;j++)Beta[j]=Hash_add(Hash_prpr(Beta[j-1],K),Alpha[j-1]-'a'+1);
    for(j=1,L=s[i].length(),ans=0;j<=L;j++)
    {
        Zeta[j]=Hash_add(Hash_prpr(Zeta[j-1],K),s[i][j-1]-'a'+1);
        if(j>=lens&&Hash_check(Zeta,j-lens+1,j)==Beta[lens])ans++;
    }
    for(k=1;k<=lens;k++)Gamma[Alpha[k-1]-'a']+=(Hash_add(Hash_prpr(Hash_add(Hash_prpr(Hash_check(Zeta,L-k+2,L),K),Alpha[k-1]-'a'+1),sl[lens-k]),Zeta[lens-k])==Beta[lens]);
    ans=prpr(ans,ksm(2,num-i));
    for(k=0;k<26;k++)ans=(ans+prpr(Gamma[k],prpr(d[k].cheak(num)-d[k].cheak(i),inv[n-num])))%M;
    return (ans%M+M)%M;
}

int main()
{
    init();
    for(;q;q--)printf("%d\n",work());
    return 0;
}
posted @ 2021-01-02 21:20  zjjws  阅读(304)  评论(0)    收藏  举报