2/27 练习赛总结

赛时过程

ABCDE 五题,ACM / ICPC 赛制,线上赛,两个半小时。

A 题签到,手画了一棵树就瞬间 AC。

开 B,感觉是神仙题,这时看到数论神仙 Leasier 已经 A 了 D,于是妄想先切 D 题

开 D,显然数论,口胡了一下结果样例都没过,就爬回去看 B。

回到 B 题,感觉不是 dp 就是组合,dp 方程推不出来,然后就想组合数解法,就 A 了。

C 题一眼差分,然后画了几个数组,也 A 了

D 题忽然想到 gcd ⁡ \gcd gcd 的另一种分解质因数的求法,愉快地 AC。

E 题是 smg,爪巴了。

最后…
我 tmd 为什么是 rk1??

赛后总结

提交次数还是多了,以后最好一次性 AC。

赛后题解

A 题是 sb 题,就不说了。

B 题:

  • 在 [ 1 , n ] [1,n] [1,n] 中选数 m m m 次,可重复。
  • 设选出的序列排序后为 a 1 , a 2 , . . . , a m a_1,a_2,...,a_m a1​,a2​,...,am​。
  • 定义序列的贡献为 max ⁡ { a 1 , a 2 , . . . , a m } − min ⁡ { a 1 , a 2 , . . . , a m } \max\{a_1,a_2,...,a_m\}-\min\{a_1,a_2,...,a_m\} max{a1​,a2​,...,am​}−min{a1​,a2​,...,am​}。
  • 求出所有本质不同的序列的贡献和,答案对 998244353 998244353 998244353 取模。

现在假设已经选出了一个序列中的 max ⁡ \max max 和 min ⁡ \min min,那么显然剩下 m − 2 m-2 m−2 个数就只能在 [ min ⁡ , max ⁡ ] [\min,\max] [min,max] 之间选出,由于是可重复,所以套用可重复组合数的公式即可,剩下 m − 2 m-2 m−2 个数的方案数为 C max ⁡ − min ⁡ + 1 + m − 2 − 1 m − 2 C_{\max-\min+1+m-2-1}^{m-2} Cmax−min+1+m−2−1m−2​,化简得 C max ⁡ − min ⁡ + m − 2 m − 2 C_{\max-\min+m-2}^{m-2} Cmax−min+m−2m−2​,算这个只需要预处理阶乘然后用逆元来算即可。

接下来看 max ⁡ \max max 和 min ⁡ \min min 要怎么枚举,显然二重循环暴力是不可行的,那么观察上式发现只用得着 max ⁡ − min ⁡ \max-\min max−min,于是只用枚举 max ⁡ − min ⁡ \max-\min max−min,对于每个 max ⁡ − min ⁡ \max-\min max−min 有 n − ( max ⁡ − min ⁡ + 1 ) + 1 n-(\max-\min+1)+1 n−(max−min+1)+1 种选法,化简得 n − ( max ⁡ − min ⁡ ) n-(\max-\min) n−(max−min),而贡献即为 max ⁡ − min ⁡ \max-\min max−min。

所以最终答案就是:
∑ i = 1 n − 1 C i + m − 2 m − 2 × ( n − i ) × i \sum_{i=1}^{n-1}C_{i+m-2}^{m-2}\times(n-i)\times i i=1∑n−1​Ci+m−2m−2​×(n−i)×i

#include <cstdio>
#include <iostream>

using namespace std;

typedef long long ll;
const ll mod = 998244353;

ll fac[1000005];

inline void init_fac (int n) {
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
		fac[i] = fac[i - 1] * i % mod;
}

inline ll quickPow (ll a, ll k, ll p) {
	ll res = 1;
	a %= p;
	while (k) {
		if (k & 1)
			res = res * a % p;
		k >>= 1;
		a = a * a % p;
	}
	return res;
}

inline ll inv (ll x, ll p) {
	return quickPow(x, p - 2, p);
}

inline ll getC (int n, int m, ll p) {
	if (n < m || n < 0 || m < 0)
		return 0;
	if (n == m || m == 0)
		return 1;
	return fac[n] * inv(fac[m] * fac[n - m] % p, p) % p;
}

int main () {
	int n, m;
	scanf("%d %d", &n, &m);
	init_fac(1e6);
	ll ans = 0;
	for (int i = 1; i < n; i++)
		ans = (ans + 1ll * (n - i) * i % mod * getC(i + m - 2, m - 2, mod) % mod) % mod;
	printf("%lld", ans);
	return 0;
}

其他的先咕着。

上一篇:P4609 [FJOI2016]建筑师 第一类斯特林数


下一篇:P3643-[APIO2016]划艇【dp】