「AtCoder Beginner Contest 170」题解

Description

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();
        }

需要被删除的元素分以下三种:

  1. 原来在堆顶,此时被弹出,答案需要被更新,故懒标对答案无影响。
  2. 原来在堆里面,即使没被弹出,答案也不会被它改变(非最大值),故懒标对答案无影响。
  3. \(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

Link

题意:给定一张地图,有障碍,每次车可以走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.}\)

posted @ 2021-07-09 22:35  StranGePants  阅读(113)  评论(3编辑  收藏  举报