题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=5339
题面
思路
观察一下可知
k
=
m
+
1
k=m+1
k=m+1
将
a
i
a_i
ai从小到大排个序,再加上一个
a
0
=
0
a_0=0
a0=0那么我们需要计算
m
+
1
m+1
m+1次,每次计算仍在场上的怪物血量的k次方之和,那么最终答案就是
∑
i
=
0
m
(
∑
j
=
a
i
+
1
n
−
a
i
(
j
−
a
i
)
k
−
∑
j
=
i
+
1
m
(
a
j
−
a
i
)
k
)
\sum_{i=0}^m(\sum_{j={a_i+1}}^{n-a_i}(j-a_i)^k-\sum_{j=i+1}^m(a_j-a_i)^k)
∑i=0m(∑j=ai+1n−ai(j−ai)k−∑j=i+1m(aj−ai)k)再取个模。
由拉格朗日插值法(这里百度百科)
我们令
f
(
n
)
=
∑
j
=
1
n
j
k
f(n)=\sum_{j={1}}^{n}j^k
f(n)=∑j=1njk那么我们只需取
k
+
1
k+1
k+1个点
x
0
=
0
,
x
1
=
1
…
…
x
k
=
k
x_0=0,x_1=1……x_k=k
x0=0,x1=1……xk=k分别计算出
f
(
1
)
,
f
(
2
)
…
…
f
(
k
)
f(1),f(2)……f(k)
f(1),f(2)……f(k)。那么就有
f
(
n
)
=
(
n
−
x
1
)
(
n
−
x
2
)
…
…
(
n
−
x
k
)
(
x
0
−
x
1
)
(
x
0
−
x
2
)
…
…
(
x
0
−
x
k
)
y
0
+
(
n
−
x
0
)
(
n
−
x
2
)
…
…
(
n
−
x
k
)
(
x
1
−
x
0
)
(
x
1
−
x
2
)
…
…
(
x
1
−
x
k
)
y
1
+
…
…
(
n
−
x
0
)
(
n
−
x
1
)
…
…
(
n
−
x
k
−
1
)
(
x
k
−
x
0
)
(
x
k
−
x
1
)
…
…
(
x
k
−
x
k
−
1
)
y
k
f(n)=\frac{(n-x_1)(n-x_2)……(n-x_k)}{(x_0-x_1)(x_0-x_2)……(x_0-x_k)}y_0+\frac{(n-x_0)(n-x_2)……(n-x_k)}{(x_1-x_0)(x_1-x_2)……(x_1-x_k)}y_1+……\frac{(n-x_0)(n-x_1)……(n-x_{k-1})}{(x_k-x_0)(x_k-x_1)……(x_k-x_{k-1})}y_k
f(n)=(x0−x1)(x0−x2)……(x0−xk)(n−x1)(n−x2)……(n−xk)y0+(x1−x0)(x1−x2)……(x1−xk)(n−x0)(n−x2)……(n−xk)y1+……(xk−x0)(xk−x1)……(xk−xk−1)(n−x0)(n−x1)……(n−xk−1)yk考虑到除法不太好算,我们需要对分母求逆元(费马小定理相关)if(sum>0)x[i]=fast_pow(sum,MOD-2);else x[i]=-fast_pow(-sum,MOD-2);
然后暴力算(对于后面需要减去的部分暴力枚举算)即可。单次计算的时间复杂度是
O
(
k
2
)
O(k^2)
O(k2)
总计时间复杂度
O
(
k
3
)
O(k^3)
O(k3)
AC代码
#include<stdio.h>
#include<map>
#include<queue>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<stdlib.h>
#include<string.h>
#define MOD 1000000007
typedef long long LL;
using namespace std;
int t;
LL n,m,ans;
LL cz[100];
LL x[100],y[100];
long long fast_pow(long long target,long long p);
void pre()
{
LL te=m+2;
LL sum=0;
for(LL i=1;i<=te;i++)
{
sum+=fast_pow(i,m+1);
sum%=MOD;
y[i]=sum;
}
for(LL i=1;i<=te;i++)
{
sum=1;
for(LL j=0;j<=te;j++) if(j!=i)
{
sum*=(i-j);
if(sum>MOD)sum%=MOD;
else if(-sum>MOD)sum=-(-sum%MOD);
}
if(sum>0)x[i]=fast_pow(sum,MOD-2);else x[i]=-fast_pow(-sum,MOD-2);//记录每个分母的逆元
}
}
LL calc(LL now)
{
LL te=m+2;
LL sum,cot=0;
for(LL i=1;i<=te;i++)
{
sum=1;
for(LL j=0;j<=te;j++)
if(j!=i)
{
sum*=(n-cz[now]-j);sum%=MOD;
}
sum*=y[i];sum%=MOD;
sum*=x[i];
if(sum>0)sum%=MOD;
else sum=-(-sum%MOD);
cot+=sum;
}
for(LL i=now+1;i<=m;i++)cot-=fast_pow(cz[i]-cz[now] ,m+1);
if(cot<0)cot+=(((-cot)/MOD+1)*MOD);
cot%=MOD;
return cot;
}
int main()
{
scanf("%d",&t);
while(t--)
{
ans=0;
scanf("%lld%lld",&n,&m);
for(LL i=1;i<=m;i++) scanf("%lld",&cz[i]);
sort(cz+1,cz+m+1);
pre();//预处理求每个对应的y以及每个分母的逆元(显然分母不变)
for(LL i=0;i<=m;i++)
ans+=calc(i);//计算每次的值,累加
ans%=MOD;
printf("%lld\n",ans);
}
return 0;
}
long long fast_pow(long long target,long long p)
{
long long a[50];
a[1]=target;
for(int i=2;i<50;i++)
{
a[i]=a[i-1]*a[i-1];a[i]%=MOD;
}
long long ans=1;
for(int i=1;p;i++)
{
if(p%2) ans=(ans*a[i])%MOD;p/=2;
}
return ans;
}