目录
2018
round C
kickstart alarm
题目大意为根据一个数组A,A中共有N个数分别为A1,A2,。。。。,AN,利用其所有的连续子串来计算一组数P,P共有k个数,对于第k个数,起计算方法如下公式所示:
假设对于数组A,其所有可能的连续子串共有Ω个,令Ak,A(k+1),A(k+2),。。。,A(j)为其中第i个可行的子串,则其子串的指数和形式为如下的公式,结果记为Gi
那么数Pk为所有Gi的和。相应的例子可以在题目链接中看到。
题目求解方法:
对于小数据集可以暴力求解,运行时间为O(N^3*K),求解代码如下:
def kickstartalarm():
N, K, x1, y1, C, D, E1, E2, F = map(int, input('input N,K,x1,y1,C,D,E1,E2,F').split(' '))
x = [x1]
y = [y1]
A = [x1 + y1]
for i in range(1, N):
x.append((C * x[i - 1] + D * y[i - 1] + E1) % F)
y.append((D * x[i - 1] + C * y[i - 1] + E2) % F)
A.append((x[i] + y[i])%F)
constnumber=1000000007
P=[0]*K
res=0
for k in range(K):
for l in range(N):
for r in range(l,N):
for j in range(l,r+1):
P[k]+=A[j]*pow(j-l+1,k+1)
result=sum(P)%1000000007
return result,res
对于大数据集,由于存在时间限制,因此不可能用这样的方法,因此需要用巧一些的方法。因此我们对Pk的计算进行分析
有题目可知,Pk最终可以写为,其中Θ为多项式中Ai出现的总次数,现在分析Ai的乘积项m可能出现的数
对于包含Aj的数组,其长度范围为1(仅有Aj)到N(全部),在长度为N的数组里,m最大只能取到j,因此对任意子数组有1<=m<=j,接下来分析当m固定时总共会出现几次,即Θ的总数。在包含Aj的一个数组子串中,m表示Aj在该子串中的位置,分析下面两图:
m=1时
m=2时
即对于m可取的值,都会出现N-j+1次。那么Pk就可以写为,接下来考虑Pk的总和,提取公因式Aj和N-j+1,可改写为下列形式
因此可以利用等比数列求和公式来计算,由于k可能很大,因此在求幂的过程中,应该用快速幂的方法求解,同时注意到公式中中相邻的Ai,Ai+1,Ai+1括号内的数据比Ai的多了一项j+1,因此计算中不必每次都从1计算等比数列,可以记录下来减少计算。
对于等比数列求和,利用费马小定理可以将求模运算改写为下:
(b/a)%n=(b*a**(n-2))%n
同时,对于求幂过程中也利用求模公式减少计算。代码如下:耗时O(N*log(K))
def kickstartalarmBig():
constmod = 1000000007
def power(base, n):
if n == 0:
return 1
else:
result = power(base, n >> 1)
if n & 1:
return ((result * result) % constmod * base) % constmod
return (result * result) % constmod
N, K, x1, y1, C, D, E1, E2, F = map(int, input().split(' '))
x = [x1]
y = [y1]
A = [(x1 + y1)%F]
for i in range(1, N):
x.append((C * x[i - 1] + D * y[i - 1] + E1) % F)
y.append((D * x[i - 1] + C * y[i - 1] + E2) % F)
A.append((x[i] + y[i]) % F)
result = 0
lastsum = 0
for i in range(1, N + 1):
if i == 1:
lastsum += K
else:
# 费马小定理
# (b/a)%n=(b*a**n-2)%n
lastsum += i * (power(i, K) - 1) % constmod * power(i - 1, constmod - 2) % constmod
lastsum