【YBT2023寒假Day3 B】你的名字(数学)(拉格朗日插值)

你的名字

题目链接:YBT2023寒假Day3 B

题目大意

问你一个 n*m 的表格里,你选择行列都互不相同的三个点,费用是三个点的两两曼哈顿路径之和。
问你有多少选法的费用在 L~R 之间。

思路

首先变成求 \(1\sim R\)
接着思考一下会发现答案是这个:
\(\max(x_1,x_2,x_3)-min(x_1,x_2,x_3)+\max(y_1,y_2,y_3)-\min(y_1,y_2,y_3)\)

然后你考虑枚举 \(x\) 那边的值,然后再枚举 \(y\) 那边的值,然后在 \(2\leqslant x<n,2\leqslant y<m,i+j\leqslant R/2\) 的情况下,考虑统计它的方案。
那首先是最大值(或者最小值)放的方案,然后是中间那个放的方案,然后是点的排名:
\(3!(n-i)(i-1)3!(m-j)(j-1)\)
不过点之间是一样的,所以要除一个 \(3!\)

不难想象我们可以预处理 \((m-j)(j-1)\) 的前缀和,设其为 \(S(j)\),然后枚举 \(i\) 来做到 \(O(n)\)
考虑如何继续优化,由于随着 \(i\) 增大 \(j\) 的可选区间变化可能是一个先不变再变小,我们进行分类讨论。

如果 \(R/2\leqslant m+1\),那 \(R/2-i\leqslant m-1\),那 \((n-i)(i-1)S(\min(m-1,R/2-i))\)
那这个是关于 \(i\) \(5\) 次多项式(\(S()\) 里面是三次),那前缀和就是 \(6\) 次多项式。

如果 \(m+1<R/2\leqslant n+m-2\),那你可以分成两段:\(3!(\sum\limits_{i=2}^{R/2-m+1}(n-i)(i-1)S(m-1)+\sum\limits_{i=R/2-m+2}^{\min(n-1,R/2-2)}(n-i)(i-1)S(R/2-i))\)
那前面的提取出 \(S(m-1)\) 就两个前缀和相乘。
后面那一段你先也弄成两个前缀和相减变成 \(1\sim x\) 的形式,然后就也是 \(6\) 次多项式。

那对于这些 \(6\) 次多项式,其实我们可以轻松的求出 \(1\sim 7\) 的时候他们的值,那用这些值拉格朗日差值一下就好了。

代码

#include<cstdio>
#include<iostream>
#define ll long long
#define mo 1000000007

using namespace std;

ll n, m, L, R, inv2, inv6, jc[11];

ll ksm(ll x, ll y) {
	ll re = 1; x %= mo;
	while (y) {
		if (y & 1) re = re * x % mo;
		x = x * x % mo; y >>= 1;
	}
	return re;
}

ll sum1(ll now) {
	now %= mo;
	return now * (now + 1) % mo * inv2 % mo;
}

ll sum2(ll now) {
	now %= mo;
	return now * (now + 1) % mo * (2 * now + 1) % mo * inv6 % mo;
}

ll S(ll R, ll n) {
	ll re = n % mo * (sum1(R) - R % mo + mo) % mo;
	(re += mo - sum2(R) + sum1(R)) %= mo;
	return re;
}

ll tmp[11];

ll la(ll R, ll k) {
	if (k < 1) return 0;
	for (int i = 1; i <= 7; i++)
		tmp[i] = (tmp[i - 1] + (n % mo - i + mo) % mo * (i - 1) % mo * S(R - i, m) % mo) % mo;
	if (k <= 7) return tmp[k];
	ll re = 0;
	for (int i = 1; i <= 7; i++) {
		ll sum = 1; for (int j = 1; j <= 7; j++) if (j != i) sum = sum * (i - j + mo) % mo;
		(re += tmp[i] * ksm(k - i, mo - 2) % mo * ksm(sum, mo - 2) % mo) %= mo;
	}
	for (int i = 1; i <= 7; i++)
		re = re * ((k - i) % mo) % mo;
	return re;
}

ll slove(ll R) {
	R = min(R / 2, n + m - 2);
	if (R <= m + 1) return la(R, min(R - 2, n - 1));
		else {
			ll re = S(m - 1, m) * S(R - m + 1, n) % mo;
			(re += la(R, min(n - 1, R - 2))) %= mo;
			(re += mo - la(R, R - m + 1)) %= mo;
			return re;
		}
}

int main() {
	freopen("table.in", "r", stdin);
	freopen("table.out", "w", stdout);
	
	jc[0] = 1; for (int i = 1; i <= 10; i++) jc[i] = jc[i - 1] * i % mo;
	inv2 = ksm(2, mo - 2); inv6 = ksm(6, mo - 2);
	
	scanf("%lld %lld %lld %lld", &n, &m, &L, &R);
	printf("%lld", (slove(R) - slove(L - 1) + mo) % mo * 6 % mo);
	return 0;
}
posted @ 2023-01-31 15:12  あおいSakura  阅读(14)  评论(0编辑  收藏  举报