【TJOI2018】【BZOJ5339】【LOJ2578】教科书般的亵渎

【题目链接】

【前置技能】

  • k次幂前缀和
  • 拉格朗日插值法

【题解】

  • 题目大概要求的是若干 i = 1 x i k + 1 的和,还要减去其中没出现的值。
  • k 次幂前缀和 i = 1 x i k 是个关于 x k + 1 次多项式,然后直接插值即可。
  • 时间复杂度 O ( T M 3 )

【代码】

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL  long long
#define MAXN    60
#define mod 1000000007
using namespace std;
int Q, m, k, x[MAXN], y[MAXN];
LL n, len, a[MAXN], ans;

template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
    x = 0; int f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

LL qpow(LL a, int b){
    a = a % mod;
    LL ret = 1;
    while (b){
        if (b & 1) ret = ret * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ret;
}

LL calc(LL cur,  int n){
    LL ans = 0;
    for (int i = 1; i <= n; ++i){
        LL tmp = 1, tnp = 1;
        for (int j = 1; j <= n; ++j){
            if (i == j) continue;
            tmp = tmp * (cur - x[j]) % mod;
            tnp = tnp * (x[i] - x[j])% mod;
        }
        ans = (ans + y[i] * tmp % mod * qpow(tnp, mod - 2) % mod) % mod;
    }
    return ans;
}

int main(){
    read(Q);
    while (Q--){
        read(n), read(m);
        for (int i = 1; i <= m; ++i)
            read(a[i]);
        sort(a + 1, a + 1 + m);
        a[0] = 0;
        k = m + 1;
        ans = 0;
        y[0] = 0;
        for (int i = 1; i <= k + 2; ++i)
            x[i] = i, y[i] = (y[i - 1] + qpow(i, k)) % mod;
        for (int i = 0; i <= m; ++i){
            len = n - a[i];
            ans = (ans + calc(len, k + 2)) % mod;
            for (int j = i + 1; j <= m; ++j)
                ans = (ans - qpow(a[j] - a[i], k)) % mod;
        } 
        printf("%lld\n", (ans + mod) % mod);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/six_solitude/article/details/80723879