「赛后总结」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}
\]