AtCoder Beginner Contest 231 A~F 题解
上分难,上分难,上分路,今安在/ll
还好 E 题最后 5min 完成绝杀避免掉分。
一共 10 发罚时,菜的真实。
A - Water Pressure
输出 \(\frac{x}{100}\) 即可。
B - Election
用 map 统计每个字符串的出现次数,然后暴力判断一下哪个字符串出现次数最多
C - Counting 2
给你 \(n\) 个数, \(q\) 次询问,每次询问给定一个 \(x\) ,回答有多少个数 \(\ge x\) 。
\(1 \le n,q \le 2 \times 10^5\)
从小到大排完序,对于每次询问,每次 lower_bound
一下第一个 \(\ge x\) 的位置 \(k\) ,这次询问答案为 \(n-k+1\) 。
时间复杂度 \(\mathcal O((n+q)\log n)\)
D - Neighbors
给你 \(n\) 个点,要求将他们排成一个序列,但要求满足 \(m\) 个关系。
对于每个关系 \((u,v)\) 表示 \(u\) 要和 \(v\) 相邻。最后输出能否在满足所有关系的情况下排成一个序列。
首先对于每个点要满足度数 \(\le 2\),还有一种情况是关系构成一个环也是不合法的。
判环可以直接用 dfs
或者 tarjan
。
E - Minimal payments
从前有一个国家,这个国家里有 \(n\) 种货币,第 \(1\) 种货币的面值为 \(a_i\)。
在这里,\(1 = a_1 < a_2 < ... < a_{n-1} < a_n\) ,并且对于每个 \(i \in [1,n)\) ,都满足 \(a_{i+1}\) 是 \(a_i\) 的倍数。
现在你要支付 \(X\) 元,你要求出一种方案,使得支付的钱的张数和找回的钱的张数的和最少。
你只需要输出这个和最小是多少。
\(1 \le n \le 60\), \(1 = a_1 < ... < a_n \le 10^{18}\),\(1 \le X \le 10^{18}\)
注意,对于每个 \(i \in [1,n)\) ,都满足 \(a_{i+1}\) 是 \(a_i\) 的倍数。
\(n\) 的数据范围很小,考虑 dfs
,因为后面一个数是前面一个数的倍数,我们考虑从前向后 dfs
或者从后往前 dfs
。
先说官方题解的做法
可以发现,所有可能出现的数值为 \(\lfloor \frac{X}{a_i} \rfloor\) 和 \(\lceil \frac{X}{a_i} \rceil\)。加上记忆化,总体复杂度为 \(\mathcal O(n)\) 。
具体方向是从后向前枚举使用那种货币,你可以考虑将现在的 \(X\) 补成 \(Y \equiv 0 \pmod {a_i}\) 的情况,这样你需要补的金额是 \(Y-X\),你也可以考虑把能用 \(a_i\) 支付的部分全部用 \(a_i\) 支付,最后剩下 \(X \% a_i\) 的金额需要支付。
在说一下我的卡常做法
你也可以从后向前搜,在使用了第 \(i\) 种货币后要保证 剩下的金额 $ % a_{i+1} = 0$ ,这样的复杂度是 \(\mathcal O(2^{60})\) 。
然后我们加一点剪枝:
- 如果当前的答案已经超过了现有的最优的答案,直接返回。
- 如果
(double)clock()/CLOCKS_PER_SEC >= 1.98
,直接输出现有的答案退出程序。
只加第一个会 T 三个点,加了第二个之后就可以卡过去了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int INF = 1e18 + 10;
int n, X, ans = INF;
int a[100];
void dfs(int pos, int sum, int cnt) {
if((double)clock()/CLOCKS_PER_SEC >= 1.98) cout << ans << "\n", exit(0);
if(cnt >= ans) return ;
if(pos == n) {
cnt = cnt + sum / a[n];
ans = min(ans, cnt);
return ;
}
int x = sum % a[pos + 1];
if(!x) dfs(pos + 1, sum, cnt);
else {
dfs(pos + 1, sum - x, cnt + x / a[pos]);
dfs(pos + 1, sum + a[pos + 1] - x, cnt + (a[pos + 1] - x) / a[pos]);
}
}
signed main() {
cin >> n >> X;
for(int i = 1; i <= n; ++i) cin >> a[i];
dfs(1, X, 0);
cout << ans << "\n";
return 0;
}
F - Jealous Two
给你 \(n\) 个物品,每个物品有两个属性 \(a_i, b_i\),求下面这个式子的值:
\[\sum_{i=1}^{n} \sum_{j=1}^{n} [a_i \ge a_j \&\& b_i \le b_j] \]\(1 \le n \le 2 \times 10^5, 0 \le a_i, b_i \le 10^9\) 。
一个经典的二位偏序问题。
首先对 \(b\) 数组排序,消除第二个限制。
然后就变成了求 \(a\) 数组的类似逆序对的问题。
对 \(a\) 数组离散化后上树状数组即可。
有两点要注意:
- 当 \(i=j\) 时方案合法,所以最后的答案要 \(+ n\) ,(先修改在求和的写法应该不需要)
- 当两个物品的两个值都相同是,不仅 \((i,j)\) 有贡献 \((j,i)\) 也有贡献,在外面单独统计一下即可。
时间复杂度 \(\mathcal O(n \log n)\) 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 2e5 + 10;
struct node {
int x, y;
bool operator < (const node &b) const { return y == b.y ? x > b.x : y < b.y; }
}a[MAXN];
int n, Cnt = 0, ans = 0;
int date[MAXN];
namespace Bit {
int sum[MAXN];
int lb(int x) { return x & -x; }
void Modify(int x, int k) { for(; x <= Cnt; x += lb(x)) sum[x] += k; }
int Query(int x) { int res = 0; for(; x; x -= lb(x)) res = res + sum[x]; return res; }
}
signed main() {
cin >> n;
for(int i = 1; i <= n; ++i) cin >> a[i].x, date[i] = a[i].x;
for(int i = 1; i <= n; ++i) cin >> a[i].y;
sort(a + 1, a + n + 1);
int cnt = 1;
for(int i = 1; i <= n; ++i) {
if(a[i].x != a[i + 1].x || a[i].y != a[i + 1].y)
ans = ans + (cnt * cnt - cnt) / 2, cnt = 1;
else cnt ++;
}
sort(date + 1, date + n + 1), date[0] = -1000;
for(int i = 1; i <= n; ++i) if(date[i] != date[i - 1]) date[++Cnt] = date[i];
for(int i = 1; i <= n; ++i) a[i].x = lower_bound(date + 1, date + Cnt + 1, a[i].x) - date;
for(int i = 1; i <= n; ++i) {
int cnt = Bit::Query(Cnt) - Bit::Query(a[i].x - 1);
Bit::Modify(a[i].x, 1);
ans = ans + cnt;
}
printf("%lld\n", ans + n);
return 0;
}
G - Balls in Boxes
[G - Balls in Boxes] [题解]
切队说他是 GF (生成函数?)板子,反正我不会生成函数,题解也没看懂。
H - Minimum Coloring
貌似是网络流最大匹配之类的?