题目:点击打开链接
题意:给出n个数,求
分析:
即数组最大值max * max为最大值的次数,求和,由于数字顺序不影响结果,所有可以先把数组从小到大排序。
a1,a2,a3, ... ,ai = x,aj = y, ... ,an
考虑到y时,mula = a1 * a2 * ... * ai
z = n - j + 1,表示y及后面还有z个数
max = x + 1,情况有mula * [(x + 1)^z - x^z],贡献度(x + 1) * mula * [(x + 1)^z - x^z]
mula表示前面i个数可以在[1,a[k]] (1<=k<=i) 中任意取。
(x+1)^z表示后面z个数可以在[1,a[k]] (j<=k<=n)中任意取,
-x^z表示要减去后面z个数都在[1,x]中任意取的情况,即此时没有数达到x + 1,最大值不为x +1。
max = x + 2,情况有mula * [(x + 2)^z - (x + 1)^z],贡献度(x + 2) * mula * [(x + 2)^z - (x + 1)^z]
...
max = y,情况有mula * [y^z - (y - 1)^z],贡献度y * mula * [y^z - (y - 1)^z]
将贡献度求和
当x < =y时,可以化简为
mula * {y^(z+1) - x^(z+1) - f(y-1,z) + f(x-1,z)} //f(n,z) = 1^z + 2^z + 3^z + ... + n^z
p次方前n项和f(n,z) = 1^z + 2^z + 3^z + ... + n^z
可以用拉格朗日插值法或者伯努利数来做,赛后用伯努利数(复杂度为n^2)过了。其他人拉格朗日插值法都是用的杜教的板子,我看不懂。于是自己写了下,一直TLE,复杂度为n^2*logn,不知道怎么优化了,欢迎各位大佬提点。
伯努利数AC代码:
#pragma comment(linker, "/STACK:102400000,102400000")///手动扩栈
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cassert>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#include<map>
using namespace std;
#define debug test
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define inf 0x3f3f3f3f
#define eps 1e-10
#define PI acos(-1.0)
typedef pair<int,int> PII;
const ll mod = 1e9+7;
const int N = 1e3+10;
ll gcd(ll p,ll q){return q==0?p:gcd(q,p%q);}
ll qp(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
int to[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
int b[N],c[N][N],inv[N],ans,tmp;
ll calc(ll n,int k){///求和:1^k+2^k+3^k+4^k+....+n^k
n++;n%=mod;tmp=n;
ans=0;
for(int i=1;i<=k+1;i++){
ans=(ans+(ll)c[k+1][i]*b[k+1-i]%mod*n%mod)%mod;
n=(ll)n*tmp % mod;
}
ans=(ll)ans*inv[k+1] % mod;
return ans;
}
void calc_init(){
c[0][0]=1;
for (int i=1;i<N;i++){
for (int j=1;j<=i;j++)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
c[i][0]=1;
}
inv[1]=1;
for(int i=2;i<N;i++) inv[i]=(ll)inv[mod%i]*(mod-mod/i)%mod;
b[0]=1;
for(int i=1;i<N;i++){
b[i]=0;
for(int k=0;k<i;k++)
b[i]=(b[i]+(ll)c[i+1][k]*b[k]%mod)%mod;
b[i]=((ll)b[i]*(-inv[i+1]) % mod+mod)%mod;
}
}
ll n,k;
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
calc_init();
while(cin>>n>>k) cout<<calc(n,k)<<endl;
return 0;
}
拉格朗日插值法TLE代码:
#pragma comment(linker, "/STACK:102400000,102400000")///手动扩栈
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<deque>
#include<list>
#include<set>
#include<map>
using namespace std;
#define debug test
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define inf 0x3f3f3f3f
#define eps 1e-10
#define MOD 1000000007
#define PI acos(-1.0)
const int N = 1e6+10;
ll gcd(ll p,ll q)
{
return q==0?p:gcd(q,p%q);
}
int to[4][2]= {{-1,0},{1,0},{0,-1},{0,1}};
ll m,a[N],sum,fac[N],y[N];
ll qp(ll a,ll b){
ll s=1;
while(b){
if(b&1) s=s*a%MOD;
a=a*a%MOD,b>>=1;
}
return s;
}
void init() {
for(int i=2; i<N; i++) fac[i]=fac[i-1]*i%MOD; ///预处理阶乘
}
ll sv(ll n,ll k){
ll ans=0;
fac[0]=fac[1]=1,y[1]=1;
for(int i=2; i<=k+2; i++) y[i]=(y[i-1]+qp(i,k))%MOD; ///预处理求出每一项的结果
if(n<=k+2) return y[n];
ll sum=1,sig;
for(ll i=n-k-2; i<=n-1; i++) sum=sum*i%MOD;
for(ll i=1; i<=k+2; i++) ///k+1项的多项式有k+2项
{
ll fz=sum*qp(n-i,MOD-2)%MOD;///分子
ll fm=qp(fac[i-1]*fac[k+2-i]%MOD,MOD-2);///分母
if((k+2-i)%2==0) sig=1;///正负号
else sig=-1;
ans=(ans+sig*y[i]*fz%MOD*fm%MOD+MOD)%MOD;
///ans+= sig*y[i]*fz%MOD*fm%MOD;
}
return (ans%MOD+MOD)%MOD;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
init();
while(cin>>m){
sum=0;
for(int i=1; i<=m; i++) cin>>a[i];
sort(a+1,a+m+1);
ll now=1;
for(int i=1; i<=m; i++){
if(a[i]==a[i-1]){
(now*=a[i])%=MOD;
continue;
}
ll s1=(qp(a[i],m-i+2)-sv(a[i]-1,m-i+1)+MOD)%MOD;
ll s2=(qp(a[i-1],m-i+2)-sv(a[i-1]-1,m-i+1)+MOD)%MOD;
sum += ( now*(s1-s2+MOD)%MOD)%MOD;
(now*=a[i])%=MOD;
}
cout<<(sum%MOD+MOD)%MOD<<endl;
}
return 0;
}