题目描述
小豆喜欢玩游戏,现在他在玩一个游戏遇到这样的场面,每个怪的血量为ai,且每个怪物血量均不相同,小豆手里有无限张“亵渎”。亵渎的效果是对所有的怪造成1点伤害,如果有怪死亡,则再次施放该法术。我们认为血量为0怪物死亡。
小豆使用一张 “亵渎”会获得一定的分数,分数计算如下,在使用一张“亵渎”之后,每一个被亵渎造成伤害的怪会产生x^k,其中x是造成伤害前怪的血量为x和需要杀死所有怪物所需的“亵渎”的张数k。
输入输出格式
输入格式:
第一行输入一个T(T≤10),表示有多少组测试数据
每组组测试数据第一行为n,m,表示有当前怪物最高的血量n,和m种没有出现的血量
接下来m行,每行1个数ai,表示场上没有血量为ai的怪物
输出格式:
一共TT行,每行一个数, 第ii行表示第ii组测试数据中小豆的最后可以获得的分数, 因为这个分数会很大需要模10^9+7109+7
输入输出样例
输入样例#1: 复制
2
10 1
5
4 2
1
2
输出样例#1: 复制
415
135
说明
对于10%的数据,有m=0
对于20%的数据,有m≤1
对于30%的数据,有m≤2
对于40%的数据,有m≤3
对于50%的数据,有m≤4
对于60%的数据,有m≤5
对于100%的数据,有m≤50
对于100%的数据,有n≤10^13。
思路:
因为所有数是连续的,若m=0时,只要用一次亵渎就可以使所有怪物死亡,否则的话就需要用m+1张亵渎。因为缺少m种血量的怪物,所以怪物按照血量可以被分成几段,因此具体的操作,就是用1次亵渎后,所有怪物的血量都要减少,同时这里产生了一次积分,这里的积分可以分段来求,对于每一段,求出从1到段结尾的自然数幂和减去1到段开始的自然数幂和,就是这段的贡献的分数。然后重复m+1次操作直到将所有的怪物都死亡。因此这里的关键是求前n项的自然数幂和,这里采用拉格朗日插值来求。
ac代码:
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 66,mod = 1e9+7;
ll fp(ll a,ll b){//快速幂 fastpow
ll res=1;
while(b){
if(b&1){
res=res*a%mod;
}
a=a*a%mod;
b>>=1;
}
return res;
}
//ll inv[3700];
ll a[maxn];
ll y[maxn],fac[maxn],ifac[maxn],pre[maxn],suf[maxn];
/*
拉格朗日插值求自然数幂和,自然数幂和是以n为自变量的k+1阶多项式,但是多项式的具体形式我们不知道,
但是我们可以通过代入k+1个已知点,来进行插值,插值出来的结果就是自然数幂和的结果
*/
ll get(ll n,ll m){ //自然数幂和
ll lim=m+1,ans=0;//m+1 -> i^m次方对应m+1阶多项式
memset(y,0,sizeof(y));
for(int i=1;i<=lim;i++) y[i]=(y[i-1]+fp(i,m))%mod;//找k+1个已知点,即求前k个自然数幂和
pre[0] = n; suf[lim+1] = 1;
for(int i = 1;i <= lim;i++) pre[i] = 1ll*pre[i-1]*(n-i)%mod;
for(int i=lim;i >= 1;i--) suf[i]=1ll*suf[i+1]*(n-i)%mod;
//连乘的时候要求i!=j的,所以乘积中是没有i这个数的,因此要求前缀积和后缀积,然后通过两者相乘,求出不含i的乘积,这里不用除法是因为取余的原因吗,否则就需要取逆元
for(int i = 0;i <= lim;i++){
ll up = 1ll*y[i]*(pre[i-1]*suf[i+1]%mod)%mod;
ll down = 1ll*ifac[i]*ifac[lim-i]%mod;
if((lim-i) & 1) down = mod-down;//lim-i为奇数fac[lim-i]应该为负,即down应该为-down,所以这里需要加上mod
ans = (ans+1ll*up*down%mod)%mod;
}
return ans;
}
void solve(){
ll n,m;
scanf("%lld%lld",&n,&m);
memset(a,0,sizeof(a));
ll ans=0;
for(ll i = 1;i<=m;i++) scanf("%lld",&a[i]);
a[++m] = ++n;//要计算缺少点之前的自然数幂和,所以所有数之后还要加一个点
sort(a+1,a+m+1);
for(ll i = 1;i<=m;i++){
for(ll j = i;j <= m;j++)
ans=(ans+(get(a[j] - 1, m )-get(a[j - 1], m)+mod)%mod)%mod;
for(ll j = i+1;j <= m;j++)
a[j]=a[j]-a[i];
a[i]=0;
}
printf("%lld\n",ans);
}
int main()
{
// inv[1] = 1;
// for(int i = 2;i<=3600;i++)
// inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;//逆元
fac[0]=1;
for(int i = 1;i<=60;i++)
fac[i]=1ll*i*fac[i-1]%mod;//阶乘
ifac[60]=fp(fac[60],mod-2);
for(int i=60;i>=1;i--)
ifac[i-1] = 1ll*ifac[i]*i%mod;//应该是求阶乘的逆元
int t;
cin>>t;
while(t--){
solve();
}
return 0;
}