背景:
出题人的语文跟体育老师学的吧(别喷我,我真看不懂,还没有样例解释)?
伯努利数是一个好东西。
题目传送门:
https://www.luogu.org/problemnew/show/P4593
题意:
有
n
−
m
n-m
n−m只怪物,其血量为
[
1
,
n
]
[1,n]
[1,n],其中
m
m
m表示有
m
m
m种血量未出现,给出未出现的血量
a
i
,
i
∈
[
1
,
m
]
a_i,i∈[1,m]
ai,i∈[1,m],现在求将所有怪物杀死的贡献。
怎么杀怪物?每一次你可以用一张“亵渎”(据说是炉石传说里的?),对所有怪造成一点伤害,若有怪死亡,则继续生效;否则再用一张新的亵渎,直到所有怪死亡。其贡献为
∑
o
p
=
1
k
∑
i
=
1
n
x
i
k
\sum_{op=1}^{k}\sum_{i=1}^{n}x_i^k
∑op=1k∑i=1nxik。
k
k
k为杀死所有怪的用的亵渎的张数,
x
i
x_i
xi为在使用第
o
p
op
op张亵渎前编号为
i
i
i的怪的血量。
思路 1 1 1:
容易发现
k
=
m
+
1
k=m+1
k=m+1。
问题就转成了求
∑
o
p
=
1
m
+
1
∑
i
=
1
n
x
i
m
+
1
\sum_{op=1}^{m+1}\sum_{i=1}^{n}x_i^{m+1}
∑op=1m+1∑i=1nxim+1。
发现
m
m
m非常小,一段连续的血量的怪的起始和结束血量可以很容易算出来。
那么我们怎么求一个形如
∑
i
=
1
n
i
k
\sum_{i=1}^{n}i^{k}
∑i=1nik的式子。
当然用伯努利数啊。
∑
i
=
1
n
i
k
=
1
k
+
1
∑
i
=
1
k
+
1
C
k
+
1
i
⋅
B
k
+
1
−
i
⋅
(
n
+
1
)
i
\sum_{i=1}^{n}i^{k}=\frac{1}{k+1}\sum_{i=1}^{k+1}C_{k+1}^{i}\cdot B_{k+1-i}\cdot (n+1)^i
i=1∑nik=k+11i=1∑k+1Ck+1i⋅Bk+1−i⋅(n+1)i
输入的不存在的血量没有排序啊,坑了我好久。
代码 1 1 1:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define mod 1000000007
using namespace std;
LL n,ans;
int k;
LL fac[100],inv[100],Inv[100],d[100],B[100];
struct node{LL x,y;} a[100];
LL calc_C(LL n,LL m)
{
if(m>n) return 0;
return n<mod?fac[n]*Inv[n-m]%mod*Inv[m]%mod:calc_C(n/mod,m/mod)*calc_C(n%mod,m%mod)%mod;
}
void init(LL ma)
{
fac[0]=fac[1]=inv[0]=inv[1]=Inv[0]=Inv[1]=1;
for(int i=2;i<=ma;i++)
fac[i]=fac[i-1]*i%mod;
for(int i=2;i<=ma+1;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<=ma;i++) Inv[i]=Inv[i-1]*inv[i]%mod;
B[0]=1;
for(int i=1;i<=ma;i++)
{
for(int j=0;j<i;j++)
B[i]=(B[i]+B[j]*calc_C(i+1,j)%mod)%mod;
B[i]=(-inv[i+1]*B[i]%mod+mod)%mod;
}
}
LL calc(LL x)
{
LL op=1,sum=0;
x%=mod;
for(int i=1;i<=k+1;i++)
{
op=op*(x+1)%mod;
sum=(sum+calc_C(k+1,i)*B[k+1-i]%mod*op%mod)%mod;
}
return sum*inv[k+1]%mod;
}
int main()
{
int T;
init(60);
scanf("%d",&T);
while(T--)
{
scanf("%lld %d",&n,&k);
for(int i=1;i<=k;i++)
scanf("%lld",&d[i]);
sort(d+1,d+k+1);
for(int i=1;i<=k;i++)
a[i]=(node){d[i-1]+1,d[i]-1};
a[k+1]=(node){d[k]+1,n};
k++;
ans=0;
for(int i=1;i<=k;i++)
{
if(a[i].x>a[i].y) continue;
// for(int j=1;j<=k;j++)
// printf("%lld %lld\n",a[j].x,a[j].y);
// printf("\n");
while(a[i].x>0)
{
for(int j=i;j<=k;j++)
{
if(a[i].x>a[i].y) continue;
ans=(ans+(calc(a[j].y)-calc(a[j].x-1)+mod)%mod)%mod;
// printf("%lld %lld %lld %lld\n",a[j].x-1,calc(a[j].x-1),a[j].y,calc(a[j].y));
a[j].x--,a[j].y--;
}
}
for(int j=i+1;j<=k;j++)
a[j].x-=a[i].y+1,a[j].y-=a[i].y+1;
// for(int j=1;j<=k;j++)
// printf("%lld %lld\n",a[j].x,a[j].y);
// printf("\n");
}
printf("%lld\n",ans);
}
}
思路 2 2 2:
拉格朗日差值。
形如
∑
i
=
1
n
i
k
\sum_{i=1}^{n}i^{k}
∑i=1nik的式子的次数为
k
+
1
k+1
k+1,因此插入前
k
+
2
k+2
k+2个的前缀和即可。
代码 2 2 2:
跑得巨慢(当然你可以重心拉格朗日差值,我就不打了吧)。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define mod 1000000007
using namespace std;
LL n,ans;
int k;
LL d[60];
struct node{LL x,y;} a[100],b[100];
LL dg(LL x,LL k)
{
if(!k) return 1;
LL op=dg(x,k>>1);
if(k&1) return op*op%mod*x%mod; else return op*op%mod;
}
LL inv(LL x)
{
return dg(x,mod-2);
}
LL lagrange(LL x)
{
LL sum=0;
x%=mod;
for(int i=0;i<=k+1;i++)
{
LL op1=b[i].y,op2=1;
for(int j=0;j<=k+1;j++)
{
if(i==j) continue;
op1=op1*((x-b[j].x+mod)%mod)%mod;
op2=op2*((b[i].x-b[j].x+mod)%mod)%mod;
}
sum=(sum+op1*inv(op2)%mod)%mod;
}
return sum;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%lld %d",&n,&k);
LL sum=0;
for(int i=0;i<=k+2;i++)
{
sum=(sum+dg(i,k+1))%mod;
b[i]=(node){i,sum};
}
for(int i=1;i<=k;i++)
scanf("%lld",&d[i]);
sort(d+1,d+k+1);
for(int i=1;i<=k;i++)
a[i]=(node){d[i-1]+1,d[i]-1};
a[k+1]=(node){d[k]+1,n};
k++;
ans=0;
for(int i=1;i<=k;i++)
{
if(a[i].x>a[i].y) continue;
// for(int j=1;j<=k;j++)
// printf("%lld %lld\n",a[j].x,a[j].y);
// printf("\n");
while(a[i].x>0)
{
for(int j=i;j<=k;j++)
{
if(a[i].x>a[i].y) continue;
ans=(ans+(lagrange(a[j].y)-lagrange(a[j].x-1)+mod)%mod)%mod;
// printf("%lld %lld %lld %lld\n",a[j].x-1,calc(a[j].x-1),a[j].y,calc(a[j].y));
a[j].x--,a[j].y--;
}
}
for(int j=i+1;j<=k;j++)
a[j].x-=a[i].y+1,a[j].y-=a[i].y+1;
// for(int j=1;j<=k;j++)
// printf("%lld %lld\n",a[j].x,a[j].y);
// printf("\n");
}
printf("%lld\n",ans);
}
}