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\) 的出现次数,那么有:
注:字符串加法为拼接。
会发现 \(S_1\) 和 \(S_2\) 不变,每次变换就等于乘二然后加一个和 \(x\) 有关的数值,而因为 \(x\) 只有 \(26\) 种可能,所以可以用正确的复杂度处理出来,可设函数 \(g_x=F_{S_2+x+S_1}\)。
然后第一次 长度大于等于 \(|N|\) 的串 \(S\) 的贡献(答案初值) 可以 Hash 暴力计算。
我们现在以这个暴力处理出来的串当做初始的 \(S_0\)(第 \(0\) 首歌),以便之后的式子描述。
那么假设我们现在要求的是第 \(m\) 首歌(新标号)的 \(F\) 值。
然后考虑答案计算的式子,直接暴力拆开,得到的是每一项乘上一个 \(2\) 的幂次的形式:
再提取一次公因式,除 答案初值 以外,可以将不同的 \(g_x\) 提取出来,得到这样一个东西:
后面那个东西就是若干个 \(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;
}

浙公网安备 33010602011771号