穿梭时间的画面的钟 从反方向 开始移动|

tmjyh09

园龄:3年2个月粉丝:1关注:3

CSP2022 J2 练习十四

寄的死死的,被吊着打

Problem A 可见点数

欧拉函数模板题。

结论:一个点 (x,y) 是可见的当且仅当 xy

证明:

假设存在一个点 (x,y)gcd(x,y)=g>1 能被看到,那么 (x,y)(xg,yg) 都在同一条直线上,那么 (x,y) 就会被 (xg,yg) 挡住,就看不到,矛盾。

#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 创世纪

我们按照题意,若 i 限制 j,则连一条 ij 的边。

性质 1:入度为 0 的点一定不能被选(这个挺显然)

性质 2:同一个强连通分量内,一定可以选出 d2 个元素,d 是强连通分量的大小。

那么此时我们可以进行 拓扑排序(不算严格意义上的,因为可能有环),先处理入度为 0 的所有答案,同时进行标记。

然后再遍历每个元素 i,此时如果 i 没被标记过,证明 i 处在一个强连通分量中,而且其中的一个元素被标记过,所以导致 i 没被标记,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

显然我们必须知道每一个元素 i 的奇偶性才能确定是否有球,所以有两种方式来知道 i 的奇偶性。

  • 花费 ci,i 直接得知
  • 查询 ci,jci+1,j

这看起来很像一个 区间 dp,但是 n2000O(n3) 无法通过。

待更新

本文作者:tmjyh09

本文链接:https://www.cnblogs.com/tmjyh09/p/16947326.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   tmjyh09  阅读(32)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起