A - Villages: Landlines
题意简述,给出一些区间,求取最多添加多长的区间使得这些区间合并。
非常简单,按题意模拟即可。
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<pair<int, int>> v(n);
for (auto &[x, y] : v) {
int m, r;
cin >> m >> r;
x = m - r, y = m + r;
}
sort(v.begin(), v.end());
int m = v[0].first;
int ans = 0;
for (auto [l, r] : v) {
if (l > m) {
ans += l - m;
m = r;
} else {
m = max(m, r);
}
}
cout << ans << '\n';
return 0;
}
C - Grab the Seat!
题意简述。
在一个矩形的教室中,有一块屏幕,我们要求取在所有座位中,可以直接看到屏幕而不被遮挡的座位的数量。
看图
我们将两条端点进行连线,然后我们发现,在所有线后方的点都为无效点。
也是非常的简单。
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
ios::sync_with_stdio(false);
int n, m, k, q;
cin >> n >> m >> k >> q;
vector<pair<int, int>> point(k);
for (auto &[x, y] : point) cin >> x >> y;
auto calc = [](auto start, const auto end, auto step) -> void {
int y = 0;
*step = min(*step, *start - 1ll);
y++, step++, start++;
long long a = 0, b = 0;
for (auto it = start; it != end; ++it, ++y, ++step) {
auto x = *it;
if (1ll * a * y >= 1ll * b * x) {
a = x;
b = y;
}
*step = min(*step, (a * y - 1) / b);
}
};
vector<long long> rows;
vector<long long> ans;
while (q--) {
ios::sync_with_stdio(false);
cin.tie(0);
int p, x, y;
cin >> p >> x >> y;
point[p - 1] = {x, y};
rows.assign(m, 1e9);
ans.assign(m, n);
for (auto &[x, y] : point) {
rows[y - 1] = min(rows[y - 1], x * 1ll);
}
calc(rows.begin(), rows.end(), ans.begin());
calc(rows.rbegin(), rows.rend(), ans.rbegin());
long long sum = 0;
for (auto x : ans) sum += max(0ll, x);
cout << sum << '\n';
}
return 0;
}
D - Mocha and Railgun
强烈建议看这个题解,图文并茂,本人就不在给予过多的解释了。
G - Lexicographical Maximum
签到题,不做太多解释。
#include <iostream>
using namespace std;
int main() {
string s; cin >> s;
bool f = true;
for (int i = 0 ; i != s.length() - 1 ; i ++ ) {
if(s[i] == '9');
else {f = false; break;}
}
if(f) cout << s << '\n';
else for (int i = 0 ; i != s.length() - 1 ; i ++ ) {
cout << 9 ;
}
return 0;
}
I - Chiitoitsu
概率期望题目
稍微详细解释一下。
假如我们开局手中有 x
张未凑成对子的牌,在牌堆中一共有 y
张牌,在题目中有明说采取 optimal strategy
。
因此照着最优解考虑即可。
我们摸到的牌只有两种可能。
- 摸到的牌是
x
张牌中的一张。 - 不是
x
张牌的对子。
我们定义概率的期望为
那么第一种情况可以拆分为以下几个部分。
- 摸到这张牌之后,我们发现,我们把这张牌凑了对子,
x - 1
,随后我们又丢弃一张,x - 1
。一共是x - 2
。 - 同时牌堆中的牌仅仅少了一张,因此为
y - 1
。 - 由于我们在牌堆中抽到我们需要的
x
张牌中的对子的概率为 。
以上可以得到 的一个部分是 并且有 的概率拿到手。
对于第二种情况就更简单了。
把拿到的牌直接扔掉即可。所以第二种情况是 。
由于我们当前抽到一张牌需要移动一次,因此我们需要在进入下一个 的时候做出一步的动作。
那么最终的结果是
最后我们的代码就是
#include <iostream>
#include <set>
#include <vector>
using namespace std;
const int MOD = 1e9 + 7;
long long Q_power(long long a, long long b = MOD - 2,
long long m = MOD) {
long long res = 1;
while (b) {
if (b & 1) res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
long long record[14][136];
long long dfs(int need, int last) {
if (need < 0) return 0;
if (record[need][last]) return record[need][last];
long long res = 1;
res += need * 3 * Q_power(last) % MOD *
dfs(need - 2, last - 1) % MOD;
// need * 3 / last * step(...)
if (last > need * 3)
res += (last - need * 3) * Q_power(last) % MOD *
dfs(need, last - 1) % MOD;
// (last - need * 3) / last * step(...)
res %= MOD;
return record[need][last] = res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
for (int i = 1; i <= T; i++) {
string s;
cin >> s;
set<string> dict;
for (int i = 0; i < 13; i++) {
string t = "";
t += s[i * 2];
t += s[i * 2 + 1];
dict.insert(t);
}
cout << "Case #" << i << ": "
<< dfs(dict.size() * 2 - 13, 34 * 4 - 13) << '\n';
}
}
J - Serval and Essay
用并查集不断的合并,跑了一个暴力就过了~
不过,这个程序在证明上可能是错的。
我们现在像这样建图
- 首先我们只放置点 1
- 然后我们每次放一个点,就和点 1 和上一个放置的点连起来。
- 最终像我这样一直建立起来所有的点。
给出一张例图。
我们可以发现,如果采用这种方式做的话,每次必须同时完成上一个点的合并才可以进行下一次。
最终,我们发现时间复杂度是
如果我们不对其进行随机化处理的话,假若我将图的顺序完全颠倒过来,那么也就意味着每次只能合并一个点,而一共 个点,最终的时间复杂度是 。
#include <iostream>
#include <vector>
using namespace std;
vector<int> fa, sz;
const int N = 2e5 + 10;
vector<int> F[N];
int find(int x) {
if (x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
sz[x] += sz[y];
fa[y] = x;
}
int solve() {
int n;
cin >> n;
fa.resize(n);
sz.assign(n, 1);
for (int i = 0; i < n; i++) fa[i] = i;
for (int i = 0; i < n; i++) {
F[i].clear();
int k;
cin >> k;
for (int j = 0; j < k; j++) {
int x;
cin >> x;
x--;
F[i].push_back(x);
}
}
while (true) {
bool bk = true;
for (int i = 0; i < n; i++) {
int f = -1;
for (auto v : F[i]) {
if (f == -1) {
f = find(v);
} else if (f != find(v)) {
f = -1;
break;
}
}
if (f != -1 && find(i) != find(f)) {
bk = false;
merge(f, i);
}
}
if (bk) break;
}
auto ans = 0;
for (auto x : sz) ans = max(ans, x);
return ans;
}
int main() {
cin.tie(0)->sync_with_stdio(false);
int t;
cin >> t;
for (int i = 1; i <= t; i++)
cout << "Case #" << i << ": " << solve() << '\n';
return 0;
}