AtCoder Grand Contest 040 简要题解
从这里开始
A < B < E < D < C = F,心情简单.jpg。
Problem A ><
把峰谷都设成 0。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; const int N = 5e5 + 5; int n;
char s[N];
int L[N], R[N]; int main() {
scanf("%s", s + 1);
n = strlen(s + 1) + 1;
for (int i = 1; i <= n; i++)
L[i] = (s[i - 1] == '<') * (L[i - 1] + 1);
for (int i = n; i; i--)
R[i] = (s[i] == '>') * (R[i + 1] + 1);
long long ans = 0;
for (int i = 1; i <= n; i++) {
ans += max(L[i], R[i]);
// cerr << max(L[i], R[i]) << ' ';
}
printf("%lld\n", ans);
return 0;
}
Problem B Two Contests
考虑如果两个区间有包含,要么它们放在一起,要么长的那一个单独在一场考试。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; const int N = 1e5 + 5; const int inf = (signed) (~0u >> 2); typedef class Segment {
public:
int l, r; Segment() { }
Segment(int l, int r) : l(l), r(r) { } Segment operator & (Segment b) const {
return Segment(max(l, b.l), min(r, b.r));
}
int length() {
return max(r - l + 1, 0);
}
boolean operator < (Segment b) const {
return r < b.r;
}
} Segment; int n;
Segment s[N], sl[N], sr[N]; int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &s[i].l, &s[i].r);
}
sort(s + 1, s + n + 1);
int ans = 0;
sl[0] = Segment(0, inf);
for (int i = 1; i <= n; i++)
sl[i] = sl[i - 1] & s[i];
sr[n + 1] = Segment(0, inf);
for (int i = n; i; i--)
sr[i] = sr[i + 1] & s[i];
for (int i = 1; i < n; i++)
ans = max(ans, sl[i].length() + sr[i + 1].length());
for (int i = 1; i <= n; i++)
ans = max(ans, s[i].length() + (sl[i - 1] & sr[i + 1]).length());
printf("%d\n", ans);
return 0;
}
Problem C Neither AB nor BA
考虑把奇数位置上的 A 变成 B,B 变成 A,那么现在变成只有 AA 和 BB 不能消除。
考虑能够消除所有字符的必要条件是 A 或者 B 的数量不超过 $n / 2$。不难发现这个条件也是充分的。
如果不存在 C,那么直接消除 AB 即可。
考虑如果 C 只与 A 或者 B 相邻,那么先把 AB 相互消除,最后和 C 消除,如果还剩一些 C,那么把 C 两两消除。因为 A,B 的数量都不超过 $n / 2$,所以它们相互消除后剩下的字符数量不会超过 C 的数量。
否则如果 A,B 的数量一样多,那么至少有 2 个 C,消除 1 个 AC 和 1 个 BC,否则把 C 和较多一个字符消除。显然这样仍然满足条件,可以递归到 $n$ 更小的问题。
然后直接计数就行了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; #define ll long long void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
} int inv(int a, int n) {
int x, y;
exgcd(a, n, x, y);
return (x < 0) ? (x + n) : (x);
} const int Mod = 998244353; template <const int Mod = :: Mod>
class Z {
public:
int v; Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { } friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~(const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
}; Z<> qpow(Z<> a, int p) {
Z<> rt = Z<>(1), pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
} typedef Z<> Zi; const int N = 1e7 + 7; int n;
Zi fac[N], _fac[N], pw2[N]; Zi comb(int n, int m) {
return (n < m) ? (0) : (fac[n] * _fac[m] * _fac[n - m]);
} int main() {
scanf("%d", &n);
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = fac[i - 1] * i;
_fac[n] = ~fac[n];
for (int i = n; i; i--)
_fac[i - 1] = _fac[i] * i;
pw2[0] = 1;
for (int i = 1; i <= n + 1; i++)
pw2[i] = pw2[i - 1] + pw2[i - 1];
Zi ans = qpow(3, n);
for (int i = (n >> 1) + 1; i <= n; i++)
ans -= comb(n, i) * pw2[n - i + 1];
printf("%d\n", ans.v);
return 0;
}
Problem D Balance Beam
一道贪心题就有 100 种假贪心。
假设我们已经定好了顺序,设 $B_i - A_i$ 的前缀 max 为 $mx$,显然答案可以这样计算
for (int i = 1; i <= n; i++) {
if (sum >= B[i]) {
sum -= B[i];
ans += 1;
} else {
ans += 1.0 * sum / B[i];
break;
}
}
考虑枚举进入 else 语句的 $i$。计算贡献可以分为 3 部分:$[1, i), (i, pmx], (pmx, n]$,其中 $pmx$ 表示前缀 max 的位置。先考虑把所有 $A_i < B_i$ 的平衡木加入 $i$ 之后 $pmx$ 之前,然后考虑把一些移动到 i 之前,不难发现移动的代价为 $\max\{A_i, B_i\}$。排序后二分一下最多能选到哪即可。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; #define ll long long
#define ld long double template <typename T>
boolean vmax(T& a, T b) {
return (a < b) ? (a = b, true) : (false);
} const int N = 1e5 + 5;
const ll llf = 1e17;
const int inf = (signed) (~0u >> 2); ll gcd(ll a, ll b) {
return (b) ? (gcd(b, a % b)) : (a);
} typedef class Fraction {
public:
ll a, b; Fraction() : a(0), b(1) { }
Fraction(ll a, ll b) : a(a), b(b) { } boolean operator < (Fraction B) const {
return ((ld) a) * B.b - ((ld) b) * B.a < 0;
}
boolean operator > (Fraction B) const {
return ((ld) a) * B.b - ((ld) b) * B.a > 0;
}
} Fraction; #define pii pair<int, int> typedef class Item {
public:
int a, b, id; Item() { }
Item(int a, int b, int id) : a(a), b(b), id(id) { } int maxval() const {
return max(a, b);
}
boolean operator < (Item i) const {
return maxval() < i.maxval();
}
} Item; int n;
vector<Item> E;
ll _presum[N], *presum; int main() {
scanf("%d", &n);
ll _sum = 0;
for (int i = 1, a, b; i <= n; i++) {
scanf("%d%d", &a, &b);
E.emplace_back(a, b, i);
_sum += max(b - a, 0);
}
sort(E.begin(), E.end());
Fraction ans (0, 1), I (1, 1);
presum = _presum + 1;
presum[-1] = 0;
for (int i = 0; i < n; i++)
presum[i] = presum[i - 1] + E[i].maxval();
for (int j = 0; j < n; j++) {
ll sum = _sum, ts;
Item& e = E[j];
if (e.a > e.b)
sum += e.b - e.a;
int l = 0, r = n - 1, mid;
#define calc(p) (presum[p] - (p >= j) * e.maxval())
while (l <= r) {
mid = (l + r) >> 1;
ts = calc(mid);
if (ts <= sum) {
l = mid + 1;
} else {
r = mid - 1;
}
}
Fraction tmp (sum - calc(l - 1), e.b);
tmp = min(tmp, I);
tmp.a += tmp.b * (l - (l > j));
ans = max(ans, tmp);
}
ll g = gcd(ans.a, ans.b *= n);
ans.a /= g;
ans.b /= g;
printf("%lld %lld\n", ans.a, ans.b);
return 0;
}
Problem E Prefix Suffix Addition
显然可以让前缀加非 0 的位置不相交,后缀加非 0 的位置不相交,并且不会更劣。
问题等价于把原序列拆成两个序列 $\{x_i\}, \{y_i\}$,满足
$x_0 = y_0 = x_{n + 1} = y_{n + 1} = 0$
- $x_i \geqslant 0, y_i \geqslant 0, x_i + y_i = a_i$。
要求最小化 $\sum [x_i > x_{i + 1}] + [y_i < y_{i + 1}]$。
设 $f_{i, j}$ 表示考虑到序列的第 $i$ 位,$x_i$ 的值为 $j$ 的最小代价。
不难发现两个性质:
- $f_{i, j}$ 的极差小于等于 2
- $f_{i, j}$ 单调不增
然后记录一下分界点,转移二分一下就行了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; const int N = 2e5 + 5; typedef class dp_t {
public:
int v0, p1, p2, a; dp_t() { }
dp_t(int v0, int p1, int p2, int a) : v0(v0), p1(p1), p2(p2), a(a) { } int eval(int x, int na) {
#define calc(xls, fold) ((fold) + (xls > x) + (a - xls < na - x))
int rt = calc(0, v0);
if (p1 <= a)
rt = min(rt, calc(p1, v0 - 1));
if (p2 <= a)
rt = min(rt, calc(p2, v0 - 2));
return rt;
}
dp_t trans(int na) {
dp_t rt (0, 0, 0, na);
rt.v0 = eval(0, na);
int l = 0, r = na, mid;
while (l <= r) {
mid = (l + r) >> 1;
if (eval(mid, na) == rt.v0) {
l = mid + 1;
} else {
r = mid - 1;
}
}
rt.p1 = l;
r = na;
while (l <= r) {
mid = (l + r) >> 1;
if (eval(mid, na) == rt.v0 - 1) {
l = mid + 1;
} else {
r = mid - 1;
}
}
rt.p2 = l;
return rt;
}
} dp_t; int n;
int a[N]; int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
}
dp_t f (0, 1, 1, 0);
for (int i = 1; i <= n; i++)
f = f.trans(a[i]);
f = f.trans(0);
printf("%d\n", f.v0);
return 0;
}
Problem F Two Pieces
考虑用 $(x, d)$ 来表示一个状态,其中较大数在 $x$,较小数和较大数的差为 $d$。那么可以通过一些限制转化成求操作序列个数,有如下三种操作:
- 将 $x, d$ 同时加上 1
- 将 $d$ 减少 1,这个时候必须满足 $d \geqslant 2$
- 将 $d$ 设为 0.
你发现前面两个操作"很 Catalan",考虑确定前两个操作序列,然后插入第三种操作。
不难发现第 1 种操作会恰好执行 $B$ 次,考虑枚举第 $2$ 种操作的操作次数 $k$。然后能用折线法计算方案数。
如果 $N = B + k$,这种情况很 trivial,可以直接做。否则考虑 $B + k < N$ 的情况。
考虑如何插入第 3 种操作,注意到它插入后必须同时满足两个条件:
- 纵坐标到达 $B - A$。
- 不会使第二种操作不合法。
注意到它的影响相当于把一段后缀的 $d$ 减去其中第一个 $d$ 的值。所以为了满足第二个条件它必须是严格的后缀最小值,为了满足第一个条件它需要操作最后一个 $d = A - k$。
满足这之后,可以把操作 3 插入任意一个满足 $d \leqslant A - k$ 的严格后缀最小值处。这个直接插板法算方案即可。
Code
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; #define ll long long void exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
} else {
exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
} int inv(int a, int n) {
int x, y;
exgcd(a, n, x, y);
return (x < 0) ? (x + n) : (x);
} const int Mod = 998244353; template <const int Mod = :: Mod>
class Z {
public:
int v; Z() : v(0) { }
Z(int x) : v(x){ }
Z(ll x) : v(x % Mod) { } friend Z operator + (const Z& a, const Z& b) {
int x;
return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
}
friend Z operator - (const Z& a, const Z& b) {
int x;
return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
}
friend Z operator * (const Z& a, const Z& b) {
return Z(a.v * 1ll * b.v);
}
friend Z operator ~(const Z& a) {
return inv(a.v, Mod);
}
friend Z operator - (const Z& a) {
return Z(0) - a;
}
Z& operator += (Z b) {
return *this = *this + b;
}
Z& operator -= (Z b) {
return *this = *this - b;
}
Z& operator *= (Z b) {
return *this = *this * b;
}
friend boolean operator == (const Z& a, const Z& b) {
return a.v == b.v;
}
}; Z<> qpow(Z<> a, int p) {
Z<> rt = Z<>(1), pa = a;
for ( ; p; p >>= 1, pa = pa * pa) {
if (p & 1) {
rt = rt * pa;
}
}
return rt;
} typedef Z<> Zi; const int N = 2e7 + 7; int n, A, B;
Zi fac[N], _fac[N]; void init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = fac[i - 1] * i;
_fac[n] = ~fac[n];
for (int i = n; i; i--)
_fac[i - 1] = _fac[i] * i;
}
Zi comb(int n, int m) {
return (n < m) ? (0) : (fac[n] * _fac[m] * _fac[n - m]);
}
Zi calc(int x, int y) {
return comb(x + y, x) - comb(x + y, x + 1);
} int main() {
scanf("%d%d%d", &n, &A, &B);
if (!B) {
puts("1");
return 0;
}
init(n << 1);
Zi ans = 0;
for (int k = 0; k <= n - B && k <= A; k++) {
if (B + k == n) {
ans += (A == k) * calc(B - 1, A);
} else {
ans += calc(B - 1, k) * comb(n - B - k - 1 + A - k, A - k);
}
}
printf("%d\n", ans.v);
return 0;
}
AtCoder Grand Contest 040 简要题解的更多相关文章
- AtCoder Grand Contest 031 简要题解
AtCoder Grand Contest 031 Atcoder A - Colorful Subsequence description 求\(s\)中本质不同子序列的个数模\(10^9+7\). ...
- AtCoder Grand Contest 039 简要题解
从这里开始 比赛目录 Problem A Connection and Disconnection 简单讨论即可. Code #include <bits/stdc++.h> using ...
- AtCoder Grand Contest 035 简要题解
从这里开始 题目目录 Problem A XOR Circle 你发现,权值的循环节为 $a_0, a_1, a_0\oplus a_1$,然后暴力即可. Code #include <bits ...
- AtCoder Grand Contest 036 简要题解
从这里开始 比赛目录 Problem A Triangle 考虑把三角形移到和坐标轴相交,即 然后能够用坐标比较简单地计算面积,简单构造一下就行了. Code #include <bits/st ...
- AtCoder Grand Contest 037 简要题解
从这里开始 题目目录 Problem A Dividing a String 猜想每段长度不超过2.然后dp即可. 考虑最后一个长度大于等于3的一段,如果划成$1 + 2$会和后面相同,那么划成$2 ...
- AtCoder Grand Contest 038 简要题解
从这里开始 比赛目录 Problem A 01 Matrix Code #include <bits/stdc++.h> using namespace std; typedef bool ...
- AtCoder Grand Contest 021完整题解
提示:如果公式挂了请多刷新几次,MathJex的公式渲染速度并不是那么理想. 总的来说,还是自己太弱了啊.只做了T1,还WA了两发.今天还有一场CodeForces,晚上0点qwq... 题解还是要好 ...
- AtCoder Grand Contest 040
Preface 今年准备省选啥都不说了,省选题基本上都做过一遍了,开始尝试板刷AGC 这场做完就从AGC001开始吧,感觉以我的速度和来机房的频率一个礼拜做一场都谢天谢地了 A - >< ...
- 2018.09.08 AtCoder Beginner Contest 109简要题解
比赛传送门 水题大赛? 全是水题啊!!! T1 ABC333 就是判断是不是两个数都是奇数就行了. 代码: #include<bits/stdc++.h> using namespace ...
随机推荐
- 升级 ASP.NET Core 3.0 设置 JSON 返回 PascalCase 格式与 SignalR 问题
由于一些 JS 组件要求 JSON 格式是 PascalCase 格式,新版本 ASP.NET Core 3.0 中默认移除了 Newtonsoft.Json ,使用了微软自己实现的 System.T ...
- Logstash:Data转换,分析,提取,丰富及核心操作
Logstash:Data转换,分析,提取,丰富及核心操作 Logstash plugins Logstash是一个非常容易进行扩张的框架.它可以对各种的数据进行分析处理.这依赖于目前提供的超过200 ...
- Python - 条件控制、循环语句 - 第十二天
Python 条件控制.循环语句 end 关键字 关键字end可以用于将结果输出到同一行,或者在输出的末尾添加不同的字符,实例如下: Python 条件语句是通过一条或多条语句的执行结果(True 或 ...
- 在React中使用react-router-dom路由
1,路由组件的基本实现 使用React构建的单页面应用,要想实现页面间的跳转,首先想到的就是使用路由.在React中,常用的有两个包可以实现这个需求,那就是react-router和react-rou ...
- 将多个sass文件合并到一个文件中
将多个sass文件合并到一个文件中 应用场景:制作angular npm包的时候,定义的一些全局样式,自定义主题色这类的情况下,多个scss文件会要合并成一个文件并写到dist文件里,发布到仓库中. ...
- python基础编程——类和实例
在了解类和实例之前,需要先了解什么是面向对象,什么又是面向过程.面向过程是以过程为中心实现一步步操作(相互调用,类似流水线思想):面向对象是以事物为中心,某个事物可以拥有自己的多个行为,而另一个事物也 ...
- jvm默认的并行垃圾回收器和G1垃圾回收器性能对比
http://www.importnew.com/13827.html 参数如下: JAVA_OPTS="-server -Xms1024m -Xmx1024m -Xss256k -XX:M ...
- 基于Proxy的小程序状态管理
摘要: 小程序状态管理. 作者:wwayne 原文:基于Proxy的小程序状态管理 Fundebug经授权转载,版权归原作者所有. 微信小程序的市场在进一步的扩大,而背后的技术社区仍在摸索着最好的实践 ...
- IntelliJ IDEA 中使用 Lambok (注解无效问题的解决)
一,概述 Lambok可以说一个能很大提高开发效率的插件,只要在使用注解的方式就能实现很多常用的功能.如常用的@Data能在编译阶段自动生成toString方法,getter方法setter方法等. ...
- C语言之整除
除法运算符:/ 当除数和被除数都整形时,就是整除. 当浮点数和整数放到一起运算时,C语言会将整数转换成浮点数,然后进行浮点数的运算. #include<stdio.h> int main( ...