BZOJ5339: [TJOI2018]教科书般的亵渎
BZOJ5339: [TJOI2018]教科书般的亵渎
https://lydsy.com/JudgeOnline/problem.php?id=5339
分析:
- 难点在于模拟。
- 除去模拟的部分,我们需要计算\(\sum\limits_{i=1}^ni^k\)。
- 那这显然是关于\(n\)的一个\(k+2\)次多项式。
- 暴力高斯消元\(O(k^3)\)即可,每次求值\(O(k)\)
- 但这题需要计算\(k^2\)次,因此总时间复杂度和多数题解一样,都是\(O(k^3)\)
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
typedef long long ll;
#define N 60
#define mod 1000000007
ll n,a[N],A[N][N],F[N];
int m,K;
ll qp(ll x,ll y) {
ll re=1; for(;y;y>>=1,x=x*x%mod) if(y&1) re=re*x%mod; return re;
}
inline void upd(ll &x,ll y) {
x+=y; if(x>=mod) x-=mod;
}
ll S(ll n) {
if(n==0) return 0;
int i;
n=n%mod;
ll re=0,now=1;
for(i=0;i<=K+1;i++) {
re=(re+now*F[i+1])%mod;
now=now*n%mod;
}
return re;
}
void Gauss(int n) {
int i,j,k;
for(i=1;i<=n;i++) {
for(j=i;j<=n&&!A[j][i];j++) ;
if(j>n) continue;
if(i!=j) {
for(k=i;k<=n+1;k++) swap(A[i][k],A[j][k]);
}
ll del=qp(A[i][i],mod-2);
for(j=i;j<=n+1;j++) A[i][j]=A[i][j]*del%mod;
for(j=1;j<=n;j++) if(j!=i) {
del=A[j][i];
for(k=i;k<=n+1;k++) {
A[j][k]=(A[j][k]-del*A[i][k])%mod;
}
}
}
}
void solve() {
memset(A,0,sizeof(A));
scanf("%lld%d",&n,&m);
int i,j;
for(i=1;i<=m;i++) scanf("%lld",&a[i]);
sort(a+1,a+m+1);
m=unique(a+1,a+m+1)-a-1;
for(i=m;i;i--) {
if(a[i]==n) n--,m--;
else break;
}
ll now,sum;
K=m+1;
for(i=1;i<=K+2;i++) {
now=1,sum=0;
for(j=1;j<=i;j++) sum=(sum+qp(j,K))%mod;
for(j=1;j<=K+2;j++) {
A[i][j]=now;
now=now*i%mod;
}
A[i][K+3]=sum;
}
Gauss(K+2);
for(i=1;i<=K+2;i++) F[i]=A[i][K+3];
now=0;
ll ans=0; a[m+1]=n+1;
for(i=0;i<=m;i++) {
for(j=i;j<=m;j++) {
ans=(ans+S(a[j+1]-1)-S(a[j]))%mod;
}
now=a[i+1]-a[i];
for(j=i+1;j<=m+1;j++) a[j]-=now;
}
printf("%lld\n",(ans%mod+mod)%mod);
}
int main() {
int T;
scanf("%d",&T);
while(T--) solve();
}