2020-7-19校内测试总结+题解。
比赛链接
点击打开链接
比赛经历
打开A题,发现是个辛普森积分,但是我并没学辛普森积分,所以跳了。
打开B题,发现是个组合计数,跳了
打开C题,感觉是个贪心题。
继续看C,感觉像个网络流贪心之类的。但是越想越不对劲,然后发现是个sb dp题。码量一发,交了。
看B题,发现可以考虑使用组合数直接推式子,得到了 60 分的做法。接着想,发现啥都想不出来。感觉是个卷积形式,但是没啥用。于是就不知道该想什么了。莫名就10:30了。
再看A题,可以写两个部分分。然后看B题,想起来打表找规律,但是时间不够了。
预估得分:20+60+100. 与实际得分无偏差。
感觉失误在于第二题“于是就不知道该想什么了”,可能并不完全是不知道该想什么了,是因为感觉式子很麻烦,懒得写出来了。只是举了个例子观察例子,却没有把式子的形式写出来,但这就无法继续推式子了。下次一定不要怕麻烦!
考完之后发现人均打表找规律切T2。
(一)-小Q 的等离子场
题目解法
使用自适应辛普森积分即可。
代码
#include
#include
#include
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 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 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\n", cal(lp, rp, simpson(lp, rp)));
return 0;
}
(二)-小Z的城市之旅
题目解法
可以发现,上-下,左-右所得的结果固定则最终到达的点固定。可以通过最终的x坐标,最终的y坐标,总步数列出三个四元方程,有一个数不确定,那么枚举那个数然后计算即可。
但是这样复杂度是 \(\mathcal O(Tn)\) 的,很明显过不去。我们发现式子的形式是类似这样的:
发现这是个范德蒙德卷积。所以套用式子即可。
如何证明:发现这是个卷积的形式。考虑使用生成函数:\(\binom{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}=\binom{r+s}{n+m}\) 。
总结
- 范德蒙德卷积。
代码
#include
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\n");
continue ;
}
printf("%d\n", ml(binom(r, (r - x - y) / 2), binom(r, (r - x - y) / 2 + x)));
}
return 0;
}
(三)-小H的硬币游戏
题目解法
容易发现模 \(k\) 余数不同的位置没有关系,所以按模 \(k\) 的余数分类,然后 dp 即可(类似最大独立集那种 dp)。
代码
#include
#include
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\n", ans);
return 0;
}