「AtCoder Beginner Contest 170」题解
Description
Link
D
Link
题意:现有一个长度为 \(n\) 的序列 \(A\),请你输出满足以下条件的所有整数 \(A_i\) 的数量:
对于每一个整数 \(j\),(\(1\leq j\leq n\)且 \(i\neq j\)) , \(A_i \neq 0\pmod{A_j}\)。
其中 \(1\leq n\leq 2 \times 10^5\)。
Solution:题目即求数列中与其他所有数互质的数,我们通过 \(\operatorname{vis}\) 数组求得每种数的个数, 再枚举 \(A_i\) 的因数,判断是否再序列中即可求得答案。注意数列中相同的数的情况。
时间复杂度:\(\Theta(n\sqrt n)\)。
Code:
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
int n,a[200005],ans;
int vis[1000005];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
vis[a[i]]++;
}
for(int i=1;i<=n;i++)
{
int op=sqrt(a[i]);
if(a[i]==1)
{
if(vis[1]>1)
{
ans++;
}
continue;
}
for(int j=1;j<=op;j++)
{
if(a[i]%j!=0)
{
continue;
}
if(j==1)
{
if(vis[j]||vis[a[i]]>1)
{
ans++;break;
}
else{
continue;
}
}
if(vis[j]||vis[a[i]/j])
{
ans++;
break;
}
}
}
printf("%d",n-ans);
return 0;
}
E
Link
题意:\(n\) 个元素,被分到 \(m\) 个集合中,有两种操作。
- 将第 \(i\) 个元素放入第 \(j\) 个集合。
- 找出所有满足:集合内含有元素 的集合内的元素的最大值,再找其中的最小值。
操作2在每次操作1后进行,一共有 \(q\) 个操作。
其中,\(1\leq n,q\leq 10^5\)。
Solution:有集合做法,但这里介绍优先队列的方法。
对于集合,用大根堆保存最大值,对于答案,用一个小根堆保存最小值。
设 \(wz[i]\) 表示第 \(i\) 个数所在的集合,\(res[i]\) 表示第 \(i\) 个集合的答案。
如果暴力进行删除操作,时间复杂度为 \(\Theta(n)\),总的复杂度可能达到 \(\Theta(n^2)\),所以我们用懒标来进行删除操作。
设当前需要被转移的元素为 \(x\),转移到的集合为 \(y\)。
Code:
while (kin[wz[x]].size() /*有元素*/&& (wz[kin[wz[x]].top().second] != wz[x]/*已经被删除*/ || kin[wz[x]].top().second == x)/*当前元素为需要删除的元素*/) {
kin[wz[x]].pop();
}
需要被删除的元素分以下三种:
- 原来在堆顶,此时被弹出,答案需要被更新,故懒标对答案无影响。
- 原来在堆里面,即使没被弹出,答案也不会被它改变(非最大值),故懒标对答案无影响。
- 为 \(x\),直接删除,更新 \(wz[x]\) 即可,对答案无影响。
然后直接将 \(x\) 插入 \(y\),再更新答案即可。
注意:答案的堆顶集合可能最大值已经被删除,所以再加入时要先删去原先堆顶。
Code:
while (ans.top().first != res[ans.top().second]) {//res[y]已经被更新,重新加入了新的res[y]
ans.pop();
}
时间复杂度:\(\Omicron(q\log n)\)。
Code:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int, int> pp;
const int MAXN = 2e5 + 5;
int wz[MAXN], res[MAXN], n, q, val[MAXN];
priority_queue<pp> kin[MAXN];
priority_queue<pp, vector<pp>, greater<pp> > ans;
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
int x, y;
scanf("%d%d", &x, &y);
wz[i] = y;
val[i] = x;
kin[y].push((pp){ x, i });
}
for (int i = 1; i <= 200000; i++) {
if (kin[i].size()) {
res[i] = kin[i].top().first;
ans.push((pp){ res[i], i });
}
}
while (q--) {
int x, y;
scanf("%d%d", &x, &y);
while (kin[wz[x]].size() && (wz[kin[wz[x]].top().second] != wz[x] || kin[wz[x]].top().second == x)) {
kin[wz[x]].pop();
}
if (kin[wz[x]].size()) {
res[wz[x]] = kin[wz[x]].top().first;
} else {
res[wz[x]] = 0x3f3f3f3f;
}
kin[y].push((pp){ val[x], x });
res[y] = kin[y].top().first;
ans.push((pp){ res[wz[x]], wz[x] });
ans.push((pp){ res[y], y });
while (ans.top().first != res[ans.top().second]) {
ans.pop();
}
printf("%d\n", ans.top().first);
wz[x] = y;
}
return 0;
}
idea来源以及更多的注释:Link
F
题意:给定一张地图,有障碍,每次车可以走4个方向,每次最多可以走 \(k\) 步,问从起点到终点的最少次数。
Solution:首先,地图最多只有 \(10^6\) 个格子,所以先“破图为链”。其次,由于先走,走少步多次,不一定比后走,走一次多步优,所以用 \(\operatorname{DP}\) 来记录每个格子的最优状况。
优化:
if (mp[m * (xx - 1) + yy] == '@'||dis[(now.x-1)*m+now.y]+1>dis[(xx-1)*m+yy]) {
break;
}
第一句,如果走当前步数遇到障碍,那么走更多步也走不过去,跳过。
第二句,如若当前走一次比到(xx,yy)的最优次数多,固无论多少步,从当前格子走到当前方向比(xx,yy)后面的格子必然没有直接从到(xx,yy)的最优方案继续走优,这一步优化了队列内元素的个数。只用 \(\operatorname{vis}\) 数组判断是否走到优化不了到下一格的方案。
Code:
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
int n, m, k, X1, X2, Y1, Y2, cnt, dis[10000005];
struct ren {
int x, y;
};
char mp[10000005];
bool vis[10000005];
char pp[10000005];
queue<ren> q;
const int dx[] = { 1, 0, -1, 0 };
const int dy[] = { 0, 1, 0, -1 };
int main() {
memset(dis, 0x3f, sizeof(dis));
scanf("%d%d%d%d%d%d%d", &n, &m, &k, &X1, &Y1, &X2, &Y2);
for (int i = 1; i <= n; i++) {
scanf("%s", pp + 1);
for (int j = 1; j <= m; j++) {
mp[++cnt] = pp[j];
}
}
if (mp[m * (X1 - 1) + Y1] == '@' || mp[m * (X2 - 1) + Y2] == '@') {
printf("-1");
return 0;
}
ren st;
st.x = X1, st.y = Y1;
vis[m * (X1 - 1) + Y1] = 1;
dis[m * (X1 - 1) + Y1] = 0;
q.push(st);
while (!q.empty()) {
ren now = q.front();
q.pop();
if (now.x == X2 && now.y == Y2) {
printf("%d", dis[(now.x - 1) * m + now.y]);
return 0;
}
for (int i = 0; i < 4; i++) {
for (int kk = 1; kk <= k; kk++) {
int xx = now.x + kk * dx[i], yy = now.y + kk * dy[i];
if (mp[m * (xx - 1) + yy] == '@' ||
dis[(now.x - 1) * m + now.y] + 1 > dis[(xx - 1) * m + yy]) {
break;
}
if (xx >= 1 && xx <= n && yy >= 1 && yy <= m) {
if (!vis[(xx - 1) * m + yy]) {
vis[m * (xx - 1) + yy] = 1;
dis[(xx - 1) * m + yy] = dis[(now.x - 1) * m + now.y] + 1;
ren b;
b.x = xx;
b.y = yy;
q.push(b);
}
} else {
break;
}
}
}
}
printf("-1");
return 0;
}
\(\Bbb{End.}\)
\(\Bbb{Thanks}\) \(\Bbb{For}\) \(\Bbb{Reading.}\)
本文来自博客园,作者:{StranGePants},转载请注明原文链接:https://www.cnblogs.com/StranGePants/p/14992715.html