[TJOI2018]教科书般的亵渎
首先,认真读题不难发现若血量的区间 \([1,m_i]\) 连续,则只需要一张亵渎就可以杀死区间 \([1,m_i]\) 内所有怪物,所以 \(k = m+1\)。
考虑到这点,我们就可以轻松的写出式子(保证 \(a_i\) 升序):
定义 \(a_0 = 0\),有
写出了这个式子,如果你知识面不足的话,就没有然后了(至少像我这种蒟蒻是这样的)。
只需知道 \(\sum\limits_{j=1}^{n-a_{i-1}}j^{m+1}\) 是一个 \(m+2\) 次的多项式,这个式子就可以算了。
\(\sum\limits_{i=1}^{x}i^n\) 是 \(n+1\) 次多项式好像是一个比较常用的结论,但我本人并没有找到网络上的证明,所以尝试着自己证了一下。
大量公式警告。
证明: \(\sum\limits_{i=1}^{x}i^n\) 是一个 \(n+1\) 次多项式.
定义:
不失一般性,直接考虑 \(n\) 的情况。
设 \(g(x) = x^{n+1}\)。
对每一项 \(g(x)\) 使用二项式定理展开:
将上述式子全部合并起来有:
化简有:
移项有:
即为:
这个式子是一个最高项为 \(x^{n+1}\) 次多项式,即猜测成立。
综上,\(f(x) = \sum\limits_{i=1}^{x}i^n\) 是一个 \(n+1\) 次多项式。
回到原式:
\(\sum\limits_{j=1}^{n-a_{i-1}}j^{m+1}\) 是一个 \(m+2\) 次的多项式所以可以用拉格朗日插值法算出来。
拉格朗日插值的式子:
这样做是 \(\mathcal{O}(m^2)\),因为本题取值是连续的,所以可以优化到 \(\mathcal{O}(m)\)。
优化后的式子长这样:
\(Pre_i\),\(Suf_i\),\(fac_i\) 分别是预处理出来的前缀积,后缀积,阶乘。
对于原式的 \(\sum\limits_{j=i}^m(a_j-a_{i-1})^{m+1}\) 直接暴力算就可以了。
最终的时间复杂度是 \(\mathcal{O}(m^2)\) 的。
Code:
# include <map>
# include <ctime>
# include <cmath>
# include <cstdio>
# include <cstdlib>
# include <cstring>
# include <algorithm>
# define LL long long
# define reg register
const LL mod = 1e9+7;
int T;
LL a[55],n,m,k,Fac[55],Pre[55],Suf[55],f[55],ans;
inline int Mod(const int x){return x < mod ? x : x-mod;}
inline int qpow(LL x,int p=1e9+5)
{
reg LL res = 1;
for(; p ; p>>=1,x = (x*x)%mod) if(p&1) res = res*x%mod;
return res;
}
inline int Lagrange(const int x)
{
if(x <= k) return f[x];
reg LL res = 0;Suf[k+1] = 1;
for(reg int i = 1; i <= k ; ++i) Pre[i] = Pre[i-1]*(x-i)%mod;
for(reg int i = k; i >= 1 ; --i) Suf[i] = Suf[i+1]*(x-i)%mod;
for(reg int i = 1; i <= k ; ++i)
res = (res + ((((k-i)&1) ? -1LL : 1LL) * (Pre[i-1]*Suf[i+1]%mod * qpow(Fac[i-1]*Fac[k-i]%mod))%mod * f[i])%mod)%mod;
return Mod(res+mod);
}
int main()
{
Fac[0] = Pre[0] = f[1] = 1;
for(reg int i = 1; i <= 54 ; ++i) Fac[i] = Fac[i-1]*i%mod;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);k = m+3;
for(reg int i = 1; i <= m ; ++i) scanf("%d",a+i);
std::sort(a+1,a+m+1);
while(a[m] == n){m--;n--;k--;}
for(reg int i = 2; i <= k ; ++i) f[i] = (f[i-1]+qpow(i,m+1))%mod;
for(reg int i = 1; i <= m+1 ; ++i)
{
ans = Mod(ans+Lagrange(n-a[i-1]));
int aiaj = 0;
for(reg int j = i; j <= m; ++j) aiaj = Mod(aiaj+qpow(a[j]-a[i-1],m+1));
ans = Mod(Mod(ans+mod-aiaj)+mod);
}
printf("%d\n",ans);ans = 0;
}
return 0;
}

浙公网安备 33010602011771号