「赛后总结」20221022 CSP 模拟赛

「赛后总结」20221022 CSP 模拟赛

点击查看目录

赛时

省流:我改几个字节就 AK 了。

上来先看 T1,感觉像是 exgcd,就先跳到了 T4。

发现简单前缀和可以解决,于是拿到了首杀。

开始看 T2,发现就是个 01 背包,于是场切了。

然后刚 T1,发现不用 exgcd,可以直接分类讨论推式子。

然后忘了除以 \(0\):

\[100\text{pts}+\dfrac{1}{0}=\!=40\text{pts}+\text{RE}\downarrow \]

最后开始做 T4,发现正解是谔分加 DP。

但是写炸了,挂了 \(100\text{pts}\),悲。

题解

T1 天道酬勤

思路

给大家讲了半天没讲懂。

吓死我了气死我了我摆烂了我不讲了。

就是推一下式子:

\[mx+\frac{m(m-1)}{2}y=n \]

分类讨论一下 \(n,m,y\) 的奇偶性即可。

具体看代码吧。

代码

点击查看代码
namespace SOLVE {
	typedef long double ldb;
	typedef long long ll;
	typedef double db;
	int T, n, m, ans;
	inline int rnt () {
		int x = 0, w = 1; char c = getchar ();
		while (!isdigit (c)) { if (c == '-') w = -1; c = getchar (); }
		while (isdigit (c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar ();
		return x * w;
	}
	inline void In () {
		n = rnt (), m = rnt (), ans = 0;
		return;
	}
	inline void Solve () {
		if (m & 1) {
			if (n % m) ans = 0;
			else if (m == 1) ans = 1;
			else ans = n / m * 2 / (m - 1) + 1;
		}
		else {
			if (n % (m / 2)) ans = 0;
			else if (n & 1) ans = (n / (m / 2) / (m - 1) + 1) / 2;
			else ans = (n / (m / 2) / (m - 1)) / 2 + 1;
		}
		return;
	}
	inline void Out () {
		printf ("%d\n", ans);
		return;
	}
}

T2 攻擂?躺平

思路

赛时突然想到了摩尔投票法(

把这些队伍分成两拨,最后擂主的战斗力就是这两拨的战斗力之差。

因此我们要让两拨的战斗力之差尽量小,即更接近 \(\dfrac{sum}{2}\)

显然用个背包即可。

代码

点击查看代码
namespace SOLVE {
	typedef long double ldb;
	typedef long long ll;
	typedef double db;
	const ll N = 1e6 + 10;
	ll n, a[N], sum, f[N];
	inline ll rnt () {
		ll x = 0, w = 1; char c = getchar();
		while (!isdigit(c)) { if (c == '-') w = -1; c = getchar();}
		while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
		return x * w;
	}
	inline void In () {
		n = rnt();
		_for (i, 1, n) {
			a[i] = rnt();
			sum += a[i];
		}
		std::sort(a + 1, a + n + 1);
		return ;
	}
	inline void Solve () {
		f[0] = 1;
		_for (i, 1, n) {
			for_ (j, sum / 2, a[i]) {
				if(f[j] || !f[j - a[i]]) continue;
				f[j] = f[j - a[i]];
			}
		}
		return ;
	}
	inline void Out () {
		for_ (i, sum / 2, 1){
			if(f[i]){
				printf("%lld\n", sum - i * 2);
				break;
			}
		}
		return ;
	}
}

T3 魔鬼训练

思路

显然 \(k\) 可以直接二分,问题是如何 Check

发现就是个简单概率 DP,设 \(f_{i,j}\) 表示前 \(i\) 完成了 \(j\) 个任务的概率。

转移方程:

\[f_{i,j}= f_{i-1,j-1}*\frac{p[i]}{1+[(j-1)*2<k]}+f_{i-1,j}*(1-\frac{p[i]}{1+[j*2<k]}) \]

代码

点击查看代码
namespace SOLVE {
	typedef long double ldb;
	typedef long long ll;
	typedef double db;
	const ll N = 1e3 + 10;
	ll n, k;ldb p[N], f[N][N];
	inline ll rnt () {
		ll x = 0, w = 1; char c = getchar ();
		while (!isdigit (c)) { if (c == '-') w = -1; c = getchar (); }
		while (isdigit (c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar ();
		return x * w;
	}
	inline bool Check (ll md) {
		ldb sum = 0.0;
		f[0][0] = 1.0;
		_for (i, 1, n) {
			f[i][0] = f[i - 1][0] * (1.0 - p[i] / 2.0);
			_for (j, 1, i) {
				ldb q1 = (j * 2.0 < md) ? (p[i] / 2.0) : p[i];
				ldb q2 = ((j - 1) * 2.0 < md) ? (p[i] / 2.0) : p[i];
				f[i][j] = f[i - 1][j - 1] * q2 + f[i - 1][j] * (1.0 - q1);
			}
		}
		_for (i, 0, md - 1) sum += f[n][i];
		return sum >= 0.4;
	}
	inline void In () {
		k = n = rnt ();
		_for (i, 1, n) scanf ("%Lf", p + i);
		return;
	}
	inline void Solve () {
		ll l = 0, r = n;
		while (l <= r) {
			bdmd;
			if (Check (mid)) r = mid - 1, k = mid;
			else l = mid + 1;
		}
		return;
	}
	inline void Out () {
		printf ("%lld\n", k);
		return;
	}
}

T4 人文关怀

思路

最简单的一道题。

\(a_i\) 的贡献为:

\[\begin{cases} -a_i &a_i=k-1\\ 1 &others \end{cases} \]

做一个前缀和,找一个最小的左端点和最大的右端点即可。

代码

点击查看代码
namespace SOLVE {
	typedef long double ldb;
	typedef long long ll;
	typedef double db;
	const ll N = 1e6 + 10;
	ll n, k, a[N], sum[N], mn = 0, ans;
	inline ll rnt () {
		ll x = 0, w = 1; char c = getchar();
		while (!isdigit(c)) { if (c == '-') w = -1; c = getchar();}
		while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
		return x * w;
	}
	inline void In () {
		n = rnt(), k = rnt();
		_for (i, 1, n) {
			a[i] = rnt(), sum[i] = sum[i - 1];
			if(a[i] + 1 == k) sum[i] -= a[i];
			else ++sum[i];
		}
		return ;
	}
	inline void Solve () {
		_for (i, 1, n){
			mn = std::min(mn, sum[i]);
			ans = std::max(ans, sum[i] - mn);
		}
		_for (i, 1, n) ans += a[i];
		return ;
	}
	inline void Out () {
		printf("%lld\n", ans);
		return ;
	}
}

总结

今天比赛策略还可以,没有像暑假一样心态乱炸。

因为题简单。

但是像除以 \(0\) 这样的细节问题还是需要注意 ,从暑假到现在已经因为类似的问题挂上几百分了吧

希望明天可以 AK。

\[\Huge\mathfrak{The\ End} \]

posted @ 2022-10-22 19:07  K8He  阅读(55)  评论(3编辑  收藏  举报