传送门:bzoj1152
下面附赠超良心推导!
这题以前只知道结论,然后代码长这样:
#include
using namespace std;
const int N=1e5+50;
const int mod=1e4;
int n,t,len,tp;
int nxt[N],a[N],dp[N];
int main(){
int i,j,kmp;
scanf("%d%d",&n,&t);
while(t--){
memset(dp,0,sizeof(dp));
scanf("%d%d",&len,&a[1]);
dp[1]=n;tp=n;kmp=0;
for(i=2;i<=len;++i){
scanf("%d",&a[i]);
for(;kmp && a[kmp+1]!=a[i];kmp=nxt[kmp]);
kmp+= a[kmp+1]==a[i]?1:0;
tp=1ll*tp*n%mod;
dp[i]=(dp[(nxt[i]=kmp)]+tp)%mod;
}
printf("%04d\n",dp[len]);
}
}
而且总是下意识写成 mod=1e5 m o d = 1 e 5 ,导致每次交都是先WA一次。。。
现在膜了 VFK V F K 神犇的博客终于会推导了!然后用下面这份代码卡到洛谷rk1,bzoj rk4(然而ccosi并没有高超的卡常技巧)
代码如下
#include
using namespace std;
const int N=1e5+100,mod=1e4;
int n,m,que,nxt[N],pw[N],ans;
int a[N];
inline int ad(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
char c;
inline void rd(int &x)
{
c=gc();x=0;
for(;!isdigit(c);c=gc());
for(;isdigit(c);c=gc()) x=x*10+(c^48);
}
int main(){
register int i;int kmp,lim=0;pw[0]=1;
rd(n);rd(que);
for(;que;--que){
rd(m);
if(m>lim) {
for(i=lim+1;i<=m;++i) pw[i]=1ll*pw[i-1]*n%mod;
lim=m;
}
for(i=1;i<=m;++i) rd(a[i]);
nxt[1]=kmp=0;
for(i=2;i<=m;++i){
for(;a[kmp+1]!=a[i] && kmp;kmp=nxt[kmp]);
nxt[i]=(kmp+= (a[kmp+1]==a[i]));
}
for(ans=0,i=m;i;i=nxt[i]) ans=ad(ans,pw[i]);
printf("%04d\n",ans);
}
}
首先声明推导方法均仿照 VFK V F K 神犇,仅仅是记录下来供自己学习掌握,而且 LaTeX L a T e X 渲染的公式看着也顺眼许多…
符号说明:
给定集合:
直接得到 ans=∑a∈Y|a|(1n)|a| a n s = ∑ a ∈ Y | a | ( 1 n ) | a |
于是想到可以设 f(A)=∑a∈A|a|(1n)|a| f ( A ) = ∑ a ∈ A | a | ( 1 n ) | a | 来化简式子求得 f(Y) f ( Y ) 。但是 |a| | a | 在 f(A) f ( A ) 中出现了两次不是很能方便地化简。
于是设 g(A,z)=∑a∈A(zn)|a| g ( A , z ) = ∑ a ∈ A ( z n ) | a | ,对 g(A,z) g ( A , z ) 中 z z 求偏导得 g′(A,z)=∑a∈A|a|(1n)|a|z|a|−1 g ′ ( A , z ) = ∑ a ∈ A | a | ( 1 n ) | a | z | a | − 1
那么 ans=f(Y)=g′(Y,1) a n s = f ( Y ) = g ′ ( Y , 1 ) 。可以先求得 g(A,z)=∑a∈A(zn)|a| g ( A , z ) = ∑ a ∈ A ( z n ) | a | ,再对其求 z z 的偏导。
现在的任务就是找一些关于 Y Y 的等式,来求出 g(Y,1) g ( Y , 1 ) 的一个仅关于 P P 的化简式。所以观察一下 P,Y,N P , Y , N 之间的关系:
ϕ+∑a∈N∑ni=1ai=Y+N ϕ + ∑ a ∈ N ∑ i = 1 n a i = Y + N
∑a∈Nas=Y+∑a∈Y∑p∈Pap ∑ a ∈ N a s = Y + ∑ a ∈ Y ∑ p ∈ P a p
接着列出 g(A,z) g ( A , z ) 的一些计算:
将上面两个等式转换成 g(A,z) g ( A , z ) 的形式:
g(ϕ,z)+(∑ni=1zn)g(N,z)=g(Y,z)+g(N,z) g ( ϕ , z ) + ( ∑ i = 1 n z n ) g ( N , z ) = g ( Y , z ) + g ( N , z )
(zn)|s|g(N,z)=g(Y,z)+g(Y,z)g(P,z) ( z n ) | s | g ( N , z ) = g ( Y , z ) + g ( Y , z ) g ( P , z )
稍微化一化:
1+zg(N,z)=g(Y,z)+g(N,z) 1 + z g ( N , z ) = g ( Y , z ) + g ( N , z )
(zn)|s|g(N,z)=(1+g(P,z))g(Y,z) ( z n ) | s | g ( N , z ) = ( 1 + g ( P , z ) ) g ( Y , z )
大力解方程,用 g(P,z) g ( P , z ) 表示 g(Y,z) g ( Y , z ) :
g(Y,z)=(zn)|s|g(P,z)−zg(P,z)−z+1+(zn)|s| g ( Y , z ) = ( z n ) | s | g ( P , z ) − z g ( P , z ) − z + 1 + ( z n ) | s |
式子项比较多,按设分子为 h(z) h ( z ) ,分母为 q(z) q ( z ) 分别来求关于 z z 的偏导化解:
h(z)=(zn)|s|,h′(z)=|s|(1n)|s|z|s|−1 h ( z ) = ( z n ) | s | , h ′ ( z ) = | s | ( 1 n ) | s | z | s | − 1
q(z)=∑a∈P(zn)|a|−∑a∈Pz|a|+1(1n)|a|−z+1+(zn)|s| q ( z ) = ∑ a ∈ P ( z n ) | a | − ∑ a ∈ P z | a | + 1 ( 1 n ) | a | − z + 1 + ( z n ) | s |
q′(z)=∑a∈P|a|(1n)|a|z|a|−1−∑a∈P(|a|+1)(1n)|a|z|a|−1+0+|s|(1n)sz|s|−1 q ′ ( z ) = ∑ a ∈ P | a | ( 1 n ) | a | z | a | − 1 − ∑ a ∈ P ( | a | + 1 ) ( 1 n ) | a | z | a | − 1 + 0 + | s | ( 1 n ) s z | s | − 1
z=1 z = 1 ,得:
h(1)=(1n)|s|,h′(1)=|s|(1n)|s| h ( 1 ) = ( 1 n ) | s | , h ′ ( 1 ) = | s | ( 1 n ) | s |
q(1)=(1n)|s|,q′(1)=|s|(1n)|s|−∑a∈P(1n)|a|−1 q ( 1 ) = ( 1 n ) | s | , q ′ ( 1 ) = | s | ( 1 n ) | s | − ∑ a ∈ P ( 1 n ) | a | − 1
带回原式:
g′(Y,1)=h′(1)q(1)−h(1)q′(1)q(1)q(1) g ′ ( Y , 1 ) = h ′ ( 1 ) q ( 1 ) − h ( 1 ) q ′ ( 1 ) q ( 1 ) q ( 1 )
大力带入计算,奇妙的事情发生了,得到这样的一个化简式:
g′(Y,1)=∑a∈P(1n)|a|+1(1n)|s|=∑a∈Pn|s|−|a|+n|s| g ′ ( Y , 1 ) = ∑ a ∈ P ( 1 n ) | a | + 1 ( 1 n ) | s | = ∑ a ∈ P n | s | − | a | + n | s |
由前面定义的数字串集合 P P 的性质发现 |s|−|a| | s | − | a | 实际上枚举到了 Q Q 中 s s 以外的所有数字串的长度,而 s s 也在另一项 n|s| n | s | 中补上了,所以最终:
ans=f(Y)=g′(Y,1)=∑a∈Qn|a| a n s = f ( Y ) = g ′ ( Y , 1 ) = ∑ a ∈ Q n | a |
数字串集合 Q Q 对 s s 做一遍 kmp k m p 即可求得,所以除开预处理 n n 的次幂,复杂度是 O(n) O ( n ) 的。
话说建立 kmp k m p 的 next n e x t 数组的复杂度怎么证啊。这里是一个自己脑补的证明:
建立过程中每次加入一个字符, kmp k m p 最多后移一位,每次匹配失败,跳 next n e x t 链至少会前移一位(除非已经到了初始位置)。设总共了 x x 次 next n e x t 链,字符串长度为 n n ,则 nextn≤n−x n e x t n ≤ n − x 。由 0≤nextn<n 0 ≤ n e x t n < n 得 x≤n x ≤ n ,建立总复杂度 x+n≤2n x + n ≤ 2 n ,没有仔细想,但上界应该很难卡满。
代码按式子代入计算即可。