P4548 [CTSC2006]歌唱王国
题目描述
Solution
这一题在《具体数学(混泥土数学)》里讲得很详细了啊,这里相当于总结一下,想具体了解的直接看书吧。
我们先考虑字符集为 2 2 2的情况,设硬币正面朝上 ( H ) (H) (H)的概率为 p p p,反面朝上 ( T ) (T) (T)的概率为 q q q。
这道题显然需要建立一个类似 K M P KMP KMP的自动机,令 S i S_i Si表示已经到达目标串的第 i i i个位置的状态集合。
例如目标串为
T
H
T
T
H
THTTH
THTTH(以下都以这个串为例介绍这种做法),则
S
2
=
{
T
H
,
T
T
H
,
H
T
H
,
T
T
T
H
.
.
.
.
.
.
}
S
5
=
{
T
H
T
T
H
,
T
T
H
T
T
H
,
H
T
H
T
T
H
.
.
.
.
.
.
}
S_2=\{TH,TTH,HTH,TTTH......\}\\ S_5=\{THTTH,TTHTTH,HTHTTH......\}
S2={TH,TTH,HTH,TTTH......}S5={THTTH,TTHTTH,HTHTTH......}
显然有:
S
0
=
1
+
S
0
H
+
S
1
T
+
S
2
H
S
1
=
S
0
T
+
S
4
T
S
2
=
S
1
H
+
S
3
H
S
3
=
S
2
T
S
4
=
S
3
T
S
5
=
S
4
H
S_0=1+S_0H+S_1T+S_2H\\ S_1=S_0T+S_4T\\ S_2=S_1H+S_3H\\ S_3=S_2T\\ S_4=S_3T\\ S_5=S_4H\\
S0=1+S0H+S1T+S2HS1=S0T+S4TS2=S1H+S3HS3=S2TS4=S3TS5=S4H
其中
1
1
1表示空状态,像
S
1
H
S_1\;H
S1H这种形式表示在
S
1
S_1
S1状态集合每一个元素后面加一个
H
H
H字符之后的状态。
这样的话,我们就能有一个
O
(
n
3
)
O(n^3)
O(n3)的算法。
令
E
[
x
]
E[x]
E[x]表示达到目标串的第
x
x
x个位置所需要的字符个数的期望,直接按照上面的状态转移高斯消元计算即可。
但这显然是不够的。
我们设 N = ∑ i < n S i , S = S n N=\sum_{i<n} S_i,S=S_n N=∑i<nSi,S=Sn,表示还没有目标串的所有状态集合,考虑用只用 S S S和 N N N,表示出上面的式子。
于是我们可以推出:
1
+
N
(
H
+
T
)
=
S
+
N
1+N(H+T)=S+N
1+N(H+T)=S+N
因为
1
+
N
1+N
1+N加上一个字符之后的状态要么没达到目标,要么达到目标,根据定义显然
S
S
S和
N
N
N不相交。
并且有:
N
T
H
T
T
H
=
S
+
S
T
T
H
N\;\;THTTH=S+S\;\;TTH
NTHTTH=S+STTH
这一个式子我理解了很久(
Q
A
Q
QAQ
QAQ)。。
因为
N
N
N中的元素加了
T
H
T
T
H
THTTH
THTTH之后一定能达到目标状态,然而可能
N
N
N中的元素加了
T
H
TH
TH之后就已经达到目标,这样就会在末尾多一个
T
T
H
TTH
TTH,且容易发现
S
S
S和
S
T
T
H
S\;\;TTH
STTH仍是不相交的。
我们考虑这种加了长度为 k k k的目标串,就包含目标串的条件,显然是 S T [ 1.. k ] = S T [ n − k + 1 , k ] ST[1..k]=ST[n-k+1,k] ST[1..k]=ST[n−k+1,k],就是说 S T ST ST有一段长度为 k k k的 B o r d e r Border Border。
有了这两个式子之后,我们就可以推出:
(
1
−
S
)
(
1
−
H
−
T
)
T
H
T
T
H
=
S
(
1
+
T
T
H
)
(1-S)(1-H-T)\;\;THTTH=S(1+TTH)
(1−S)(1−H−T)THTTH=S(1+TTH)
事实上,这里的 S S S, T T T和 H H H,就不是单纯的状态了,这里的 S S S已经是一个概率生成函数,而 T T T和 H H H能够表示一个概率生成函数的转移:
设
G
(
z
)
G(z)
G(z)为一个表示抛掷次数的概率生成函数。
G
(
z
)
=
∑
i
P
r
(
z
=
i
)
z
i
G(z)=\sum_i Pr(z=i)z^i
G(z)=i∑Pr(z=i)zi
其中的
z
z
z就是表示抛掷次数的形式变元,因为下一个字符为
T
T
T的概率为
q
q
q,为
H
H
H的概率为
p
p
p,所以加一个
T
T
T相当于
G
(
z
)
∗
p
z
G(z)*pz
G(z)∗pz,加一个
H
H
H相当于
G
(
z
)
∗
q
z
G(z)*qz
G(z)∗qz。
于是上式变成了
(
1
−
G
(
z
)
)
(
q
−
p
z
−
q
z
)
−
1
p
3
q
2
z
5
=
G
(
z
)
(
1
+
p
2
q
z
)
(1-G(z))(q-pz-qz)^{-1}p^3q^2z^5=G(z)(1+p^2qz)
(1−G(z))(q−pz−qz)−1p3q2z5=G(z)(1+p2qz)
解得:
G
(
z
)
=
p
3
q
2
z
5
(
1
+
p
2
q
z
3
)
(
1
−
z
)
+
p
3
q
2
z
5
G(z)=\frac{p^3q^2z^5}{(1+p^2qz^3)(1-z)+p^3q^2z^5}
G(z)=(1+p2qz3)(1−z)+p3q2z5p3q2z5
而我们要求的期望为
E
(
x
)
=
G
′
(
1
)
E(x)=G'(1)
E(x)=G′(1),因此我们需要想方法求出
G
′
(
1
)
G'(1)
G′(1)。
有一个特殊的方法是:
设
F
(
z
)
=
z
5
/
G
(
z
)
F(z)=z^5/G(z)
F(z)=z5/G(z)
可以证明
F
(
z
)
F(z)
F(z)的均值(
M
e
a
n
Mean
Mean)和
G
(
z
)
G(z)
G(z)的均值相同。
因此
M
e
a
n
(
G
)
=
M
e
a
n
(
z
5
)
−
M
e
a
n
(
F
)
=
p
−
1
q
−
1
+
p
−
3
q
−
2
Mean(G)=Mean(z^5)-Mean(F)=p^{-1}q^{-1}+p^{-3}q^{-2}
Mean(G)=Mean(z5)−Mean(F)=p−1q−1+p−3q−2
若 p = q = 1 / 2 p=q=1/2 p=q=1/2,则有期望 6 6 6次到达目标串。
而根据上面的式子,容易知道,若存在一个 B o r d e r Border Border有 x x x个 H H H, y y y个 T T T,则答案 A n s + = p − y q − x Ans+=p^{-y}q^{-x} Ans+=p−yq−x,所以只要知道有哪些长度的 B o r d e r , Border, Border,就能算出答案。
我们已经解决了字符集大小为 2 2 2的问题,现在字符集大小为 n n n的做法也是一样的,因为这里的所有字符等概率出现,所以就不需要继续推到,相当于每出现一个 B o r d e r Border Border的贡献为 n k n^k nk,直接统计 B o r d e r Border Border长度,计算贡献和即可。
时间复杂度 O ( n ) O(n) O(n),有点小精度问题,我开 l o n g d o u b l e , W A long\;\;double,WA longdouble,WA开 d o u b l e , A C double,AC double,AC,表示一脸懵逼。
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>
#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second
using namespace std;
template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }
typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;
const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int Mod=10000;
const int mods=1e9+7;
const int MAXN=600005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
int f=1,x=0; char c=getchar();
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
return x*f;
}
int Pow[MAXN],hsh[MAXN],a[MAXN];
inline int upd(int x,int y,int mods){ return x+y>=mods?x+y-mods:x+y; }
inline int gethash(int l,int r) { return upd(hsh[r],mods-1ll*Pow[r-l+1]*hsh[l-1]%mods,mods); }
int main()
{
int C=read(),Case=read();
while (Case--)
{
int n=read();
for (int i=1;i<=n;i++) a[i]=read();
Pow[0]=1;
for (int i=1;i<=n;i++) Pow[i]=1ll*Pow[i-1]*(C+1)%mods;
for (int i=1;i<=n;i++) hsh[i]=(1ll*hsh[i-1]*(C+1)+a[i])%mods;
int ans=0;
for (int i=1,p=C%Mod;i<=n;i++,p=p*C%Mod)
if (gethash(1,i)==gethash(n-i+1,n)) ans=upd(ans,p,Mod);
printf("%04d\n",ans);
}
return 0;
}