NOI模拟(5.23) TJOID2T3 教科书般的亵渎 (bzoj5339)

教科书般的亵渎

题目背景:

5.23 模拟 TJOI2018D2T3

分析:拉格朗日差值

 

这个题的题意怕不是有毒,注意他每一次的k是相同的,为总共要用多少次卡片,然后卡片是每次先将所有的数减一,如果这一次减一的过程中有一个数变成了0,那么就会把所有数再次减1,直到一次没有数被减为0,或者所有数都为0才算是一次卡片的效果停止,比如样例的第一组数据,用完一次卡片之后原来为1 ~ 4的怪物全部都已经死了,原来为6 ~ 10的会变成1 ~ 5,考虑这道题,显然我们需要求一些ik的前缀和,显然k = m + 1也就是有多少段,而要快速求ik的前缀和,考虑到前缀和一定是一个k + 1次多项式,我们直接用拉格朗日插值,插掉前k + 2项就可以了,然后每一次询问前缀和就是O(m)的,需要询问O(m2)次,所以总复杂度为O(Tm3)。可以轻松通过。

 

Source:

 

/*
	created by scarlyw
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

inline char read() {
	static const int IN_LEN = 1024 * 1024;
	static char buf[IN_LEN], *s, *t;
	if (s == t) {
		t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
		if (s == t) return -1;
	}
	return *s++;
}

/*
template
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = read(), iosig = false; !isdigit(c); c = read()) {
		if (c == -1) return ;
		if (c == '-') iosig = true;	
	}
	for (x = 0; isdigit(c); c = read()) 
		x = ((x << 2) + x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
//*/

const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(char c) {
	if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
	*oh++ = c;
}

template
inline void W(T x) {
	static int buf[30], cnt;
	if (x == 0) write_char('0');
	else {
		if (x < 0) write_char('-'), x = -x;
		for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
		while (cnt) write_char(buf[cnt--]);
	}
}

inline void flush() {
	fwrite(obuf, 1, oh - obuf, stdout);
}

// /*
template
inline void R(T &x) {
	static char c;
	static bool iosig;
	for (c = getchar(), iosig = false; !isdigit(c); c = getchar()) {
		if (c == -1) return ;
		if (c == '-') iosig = true;	
	}
	for (x = 0; isdigit(c); c = getchar()) 
		x = ((x << 2) + x << 1) + (c ^ '0');
	if (iosig) x = -x;
}
// */

const int MAXN = 50 + 10;
const int mod = 1000000000 + 7;

#define int long long
long long t, cnt, n, m, k;
long long a[MAXN], fac[MAXN], inv_fac[MAXN], f[MAXN], num[MAXN];

inline long long mod_pow(long long a, long long b) {
    long long ans = 1;
    for (; b; b >>= 1, a = a * a % mod)
        if (b & 1) ans = ans * a % mod;
    return ans;
}

inline void solve_number(int k) {
    for (int i = 1; i <= k + 1; ++i) {
        num[i] = f[i] * inv_fac[i - 1] % mod * inv_fac[k + 1 - i] % mod;
        num[i] = (((k + 1 - i) & 1) ? mod - num[i] : num[i]);
    } 
}

inline long long sum(long long n) {
    static long long front[MAXN], behind[MAXN];
    n %= mod, front[0] = 1, behind[k + 3] = 1;
    for (int i = 1; i <= k + 2; ++i) 
        front[i] = front[i - 1] * (n - i + mod) % mod;
    for (int i = k + 2; i >= 2; --i)
        behind[i] = behind[i + 1] * (n - i + mod) % mod;
    long long ans = 0;
    for (int i = 1; i <= k + 2; ++i) 
        ans = (ans + front[i - 1] * behind[i + 1] % mod 
            * num[i]) % mod;
    return ans;
}

inline void solve() {
    R(n), R(m);
    for (int i = 1; i <= m; ++i) R(a[i]);
    std::sort(a + 1, a + m + 1), a[m + 1] = n + 1, k = m + 1, fac[0] = 1;
    for (int i = 1; i <= k + 2; ++i) 
        f[i] = (f[i - 1] + mod_pow(i, k)) % mod;
    
    for (int i = 1; i <= k + 2; ++i) fac[i] = fac[i - 1] * i % mod;
    inv_fac[k + 2] = mod_pow(fac[k + 2], mod - 2);
    for (int i = k + 1; i >= 0; --i) 
        inv_fac[i] = inv_fac[i + 1] * (i + 1) % mod;

    solve_number(k + 1);
    long long ans = 0;
    for (int i = 1; i <= k; ++i) {
        for (int j = i; j <= k; ++j)
            ans = (ans + sum(a[j] - 1) - sum(a[j - 1]) + mod) % mod;
        int l = a[i];
        // std::cout << ans << '\n';
        for (int j = i; j <= k; ++j) a[j] -= l;
    }
    std::cout << ans << '\n';
}

#undef int
int main() {
    freopen("defile.in", "r", stdin);
    freopen("defile.out", "w", stdout);
    R(t);
    while (t--) solve();
    return 0;
}

你可能感兴趣的:(NOIP解题报告,------数学-------,拉格朗日插值)