【比赛】【校内测试】2020-7-19校内测试

2020-7-19校内测试总结+题解。

比赛链接

点击打开链接

比赛经历

打开A题,发现是个辛普森积分,但是我并没学辛普森积分,所以跳了。

打开B题,发现是个组合计数,跳了

打开C题,感觉是个贪心题。

继续看C,感觉像个网络流贪心之类的。但是越想越不对劲,然后发现是个sb dp题。码量一发,交了。

看B题,发现可以考虑使用组合数直接推式子,得到了 60 分的做法。接着想,发现啥都想不出来。感觉是个卷积形式,但是没啥用。于是就不知道该想什么了。莫名就10:30了。

再看A题,可以写两个部分分。然后看B题,想起来打表找规律,但是时间不够了。

预估得分:20+60+100. 与实际得分无偏差。

感觉失误在于第二题“于是就不知道该想什么了”,可能并不完全是不知道该想什么了,是因为感觉式子很麻烦,懒得写出来了。只是举了个例子观察例子,却没有把式子的形式写出来,但这就无法继续推式子了。下次一定不要怕麻烦!

考完之后发现人均打表找规律切T2。

(一)-小Q 的等离子场

题目解法

使用自适应辛普森积分即可。

代码

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef double db;
const int MAXN = 205, MAXR = 1e4 + 5;
const db inf = 1e8, lim = 1e4, eps = 1e-8;
int n, tot;
db x[MAXN], y[MAXN], r[MAXN], R[MAXN];
struct Region {
	db ps;
	int tag;
	Region() {}
	Region(db _p, int t) : ps(_p), tag(t) {}
} store[MAXN * 4];
bool cmp(Region x, Region y) { return x.ps < y.ps; }
pair<db, db> cross(db x, db y, db r, db ps) {
	if (ps < x - r || ps > x + r)
		return make_pair(inf, inf);
	db a = abs(ps - x), len = sqrt(r * r - a * a);
	return make_pair(y - len, y + len);
}
db f(db p) {
	tot = 0;
	for (int i = 1; i <= n; ++i) {
		pair<db, db> tmp1 = cross(x[i], y[i], R[i], p), tmp2 = cross(x[i], y[i], r[i], p);
		if (tmp1.first <= lim && tmp2.first <= lim) {
			store[++tot] = Region(tmp1.first, 1);
			store[++tot] = Region(tmp2.first, -1);
			store[++tot] = Region(tmp2.second, 1);
			store[++tot] = Region(tmp1.second, -1);
		} else if (tmp1.first <= lim && tmp2.first > lim) {
			store[++tot] = Region(tmp1.first, 1);
			store[++tot] = Region(tmp1.second, -1);
		}
		//特判 
	}
	sort(store + 1, store + tot + 1, cmp);
	db ret = 0;
	int sum = 0;
	for (int i = 1; i < tot; ++i) {
		db len = store[i + 1].ps - store[i].ps;
		sum += store[i].tag;
		if (sum)
			ret += len;
	}
	return ret;
}
db simpson(db a, db b) {
	db mid = (a + b) * 0.5;
	return (b - a) * (f(a) + f(b) + 4.0 * f(mid)) / 6.0;
}
db cal(db x, db y, db ans) {
	db mid = (x + y) * 0.5;
	db lv = simpson(x, mid), rv = simpson(mid, y);
	if (fabs(lv + rv - ans) < 15.0 * eps)
		return lv + rv + (lv + rv - ans) / 15.0;
	else
		return cal(x, mid, lv) + cal(mid, y, rv);
}
int main() {
	scanf("%d", &n);
	double lp = inf, rp = -inf;
	for (int i = 1; i <= n; ++i) {
		scanf("%lf%lf%lf%lf", &x[i], &y[i], &R[i], &r[i]);
		lp = min(lp, x[i] - R[i]);
		rp = max(rp, x[i] + R[i]);
	}
	printf("%.3lf
", cal(lp, rp, simpson(lp, rp)));
	return 0;
}

(二)-小Z的城市之旅

题目解法

可以发现,上-下,左-右所得的结果固定则最终到达的点固定。可以通过最终的x坐标,最终的y坐标,总步数列出三个四元方程,有一个数不确定,那么枚举那个数然后计算即可。

但是这样复杂度是 (mathcal O(Tn)) 的,很明显过不去。我们发现式子的形式是类似这样的:

[sum_k inom{r}{m+k} inom{s}{n-k} ]

发现这是个范德蒙德卷积。所以套用式子即可。

[sum_k inom{r}{m+k} inom{s}{n-k}=inom{r+s}{m+n} ]

如何证明:发现这是个卷积的形式。考虑使用生成函数:(inom{r}{k}) 的生成函数是 (f(x)=(1+x)^r)(g(x)=(1+x)^s),那么等式左侧就相当于 ([x^{m+n}]f(x)cdot g(x)) 。也就是 ([x^{m+n}](1+x)^{r+s}=inom{r+s}{n+m})

总结

  • 范德蒙德卷积。

代码

#include <cstdio>
using namespace std;
typedef long long ll;
const int CN = 2e5 + 5, Mod = 998244353, N = 2e5;
int ml(int x, int y) { return (ll) x * y % Mod; }
int ad(int x, int y) { return (x + y > Mod) ? (x + y - Mod) : (x + y); }
int ksm(int x, int y) {
	int ret = 1;
	for (; y; y >>= 1, x = ml(x, x))
		if (y & 1) ret = ml(ret, x);
	return ret;
}
int T, r, fac[CN << 1], ifac[CN << 1];
ll x, y;
void Init() {
	fac[0] = ifac[0] = 1;
	for (int i = 1; i <= 2 * N; ++i) fac[i] = ml(fac[i - 1], i);
	for (int i = 1; i <= 2 * N; ++i) ifac[i] = ksm(fac[i], Mod - 2);
}
int binom(int x, int y) {
	if (x < 0 || y < 0 || x < y) return 0;
	return ml(fac[x], ml(ifac[y], ifac[x - y]));
}
int main() {
	scanf("%d", &T);
	Init();
	while (T--) {
		scanf("%lld%lld%d", &x, &y, &r);
		if (x > r || y > r || ((x + y + r) % 2 == 1)) {
			printf("0
");
			continue ;
		}
		printf("%d
", ml(binom(r, (r - x - y) / 2), binom(r, (r - x - y) / 2 + x)));
	}
	return 0;
}

(三)-小H的硬币游戏

题目解法

容易发现模 (k) 余数不同的位置没有关系,所以按模 (k) 的余数分类,然后 dp 即可(类似最大独立集那种 dp)。

代码

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int CN = 1e5 + 5, inf = 1e9;
int N, K, a[CN];
ll ans, f[CN][2];
int main() {
	scanf("%d%d", &N, &K);
	for (int i = 1; i <= N; ++i)
		scanf("%d", &a[i]);
	for (int i = 1; i <= K; ++i) {
		int cnt = 0;
		f[0][0] = 0;
		f[0][1] = -inf;
		for (int j = i; j <= N - K; j += K) {
			++cnt;
			f[cnt][0] = f[cnt][1] = -inf;
			f[cnt][0] = max(f[cnt - 1][0], f[cnt - 1][1]);
			f[cnt][1] = f[cnt - 1][0] + a[j] + a[j + K];
		}
		ans += max(f[cnt][0], f[cnt][1]);
	}
	printf("%lld
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/YouthRhythms/p/13347745.html