CSP2022 J2 练习十四
寄的死死的,被吊着打
Problem A 可见点数
欧拉函数模板题。
结论:一个点 是可见的当且仅当 。
证明:
假设存在一个点 , 能被看到,那么 和 都在同一条直线上,那么 就会被 挡住,就看不到,矛盾。
#include <bits/stdc++.h> using namespace std; #define all(v) v.begin(), v.end() #define rall(v) v.rbegin(), v.rend() #define sz(v) (int)v.size() #define allarr(v, len) v.begin() + 1, v.begin() + len + 1 typedef long long LL; typedef pair<int, int> PII; const int inf = 0x3f3f3f3f; const LL infLL = 0x3f3f3f3f3f3f3f3fLL; const int N = 100005; int primes[N], cnt; bool st[N]; int phi[N]; void init(int n) { phi[1] = 1; for (int i = 2; i <= n; i ++ ) { if (!st[i]) primes[cnt ++ ] = i, phi[i] = i - 1; for (int j = 0; primes[j] * i <= n; j ++ ) { st[primes[j] * i] = 1; if (i % primes[j] == 0) { phi[i * primes[j]] = phi[i] * primes[j]; break; } phi[i * primes[j]] = phi[i] * (primes[j] - 1); } } } int main() { cin.tie(nullptr) -> sync_with_stdio(false); int n; cin >> n; if (n == 1) { cout << 0 << "\n"; return 0; } init(n); LL ans = 1; for (int i = 1; i < n; i ++ ) ans += 2LL * phi[i]; cout << ans << "\n"; return 0; }
Problem B 射击
反悔贪心。
先按照时间进行排序,如果当前的窗户可以打破,就检查它是否优于堆顶元素(大根堆),如果可以就反悔,更新答案。
一定一定要判堆是否空,否则 RE。
#include <bits/stdc++.h> using namespace std; #define all(v) v.begin(), v.end() #define rall(v) v.rbegin(), v.rend() #define sz(v) (int)v.size() #define allarr(v, len) v.begin() + 1, v.begin() + len + 1 typedef long long LL; typedef pair<int, int> PII; const int inf = 0x3f3f3f3f; const LL infLL = 0x3f3f3f3f3f3f3f3fLL; const int N = 200005; int n; PII a[N]; // first: time second: val int main() { cin.tie(nullptr) -> sync_with_stdio(false); cin >> n; for (int i = 1; i <= n; i ++ ) cin >> a[i].first >> a[i].second; sort(a + 1, a + n + 1); priority_queue<int, vector<int>, greater<int>> q; LL ans = 0; for (int i = 1; i <= n; i ++ ) { if (a[i].second < 0) continue; if (a[i].first <= sz(q)) { if (a[i].second > q.top()) { ans -= q.top(); ans += a[i].second; q.pop(); q.push(a[i].second); } } else { ans += a[i].second; q.push(a[i].second); } } cout << ans << "\n"; return 0; }
Problem C 创世纪
我们按照题意,若 限制 ,则连一条 的边。
性质 1:入度为 的点一定不能被选(这个挺显然)
性质 2:同一个强连通分量内,一定可以选出 个元素, 是强连通分量的大小。
那么此时我们可以进行 拓扑排序(不算严格意义上的,因为可能有环),先处理入度为 的所有答案,同时进行标记。
然后再遍历每个元素 ,此时如果 没被标记过,证明 处在一个强连通分量中,而且其中的一个元素被标记过,所以导致 没被标记,dfs 处理出强连通分量的大小,更新答案。
#include <bits/stdc++.h> using namespace std; #define all(v) v.begin(), v.end() #define rall(v) v.rbegin(), v.rend() #define sz(v) (int)v.size() #define allarr(v, len) v.begin() + 1, v.begin() + len + 1 typedef long long LL; typedef pair<int, int> PII; const int inf = 0x3f3f3f3f; const LL infLL = 0x3f3f3f3f3f3f3f3fLL; const int N = 1e6 + 5; int n; int to[N]; int in[N]; bool vis[N]; int main() { cin.tie(nullptr) -> sync_with_stdio(false); cin >> n; for (int i = 1; i <= n; i ++ ) cin >> to[i], in[to[i]] ++ ; queue<int> q; for (int i = 1; i <= n; i ++ ) if (!in[i]) q.push(i), vis[i] = 1; int ans = 0; while (!q.empty()) { int u = q.front(); q.pop(); int v = to[u]; if (vis[v]) continue; vis[v] = 1; -- in[to[v]], ans ++ ; if (!in[to[v]] && !vis[to[v]]) q.push(to[v]), vis[to[v]] = 1; } for (int i = 1; i <= n; i ++ ) if (!vis[i]) { function<int(int)> dfs = [&](int u) { if (vis[u]) return 0; vis[u] = 1; return dfs(to[u]) + 1; }; ans += dfs(i) / 2; } cout << ans << "\n"; return 0; }
Problem D [PA2014]Kuglarz
显然我们必须知道每一个元素 的奇偶性才能确定是否有球,所以有两种方式来知道 的奇偶性。
- 花费 直接得知
- 查询 和
这看起来很像一个 区间 dp,但是 , 无法通过。
待更新
本文作者:tmjyh09
本文链接:https://www.cnblogs.com/tmjyh09/p/16947326.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步