CSP模拟8

闲话

今天老吕从国赛回来,带来一个消息:“省选可能取消,完全看 NOIP 成绩”。

不过对我没什么影响,反而还开心一些。

A. Coprime

题目大意

给定一个长度为 \(n\) 的数列 \(a\),要求出 \(1 \sim m\) 中与 \(a\) 中的所有元素互质的数。
数据范围:\(1\ \leq\ n,m\ \leq\ 10^5,1\ \leq\ a_i\ \leq\ 10^5\)

思路

模拟赛加强了数据,卡了 \(\mathcal{O}(n \sqrt{n})\),于是来写一个 \(\mathcal{O}(n \log n)\) 的。

考虑互质是什么意思,是指两个数只有一个共同因数 \(1\)。那么如果两个数有除 \(1\) 以外的共同因数,二者不互质。

所以我们可以枚举因数,若其倍数是 \(a\) 中的元素,就可以标记我们枚举的这个因数的所有倍数都是不合法的,然后可以标记。

没标记的数都是合法的,到时候直接计数输出就行了。

苏老师想到一个 \(\mathcal{O}(n)\) 的做法,在这里,看了看感觉思路十分开阔。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>

using namespace std;

const int N = 2005000;

int n,m;
int a[N];

bool t[N],p[N];
int pri[N],cnt;

queue<int> que;

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i = 1;i <= n; i++) {
        cin >> a[i];
        p[a[i]] = 1;
    }

    for(int i = 2;i <= N; i++) {
        bool flag = 0;
        for(int j = i;j <= N; j += i) {
            flag |= p[j];
        }
        for(int j = i;j <= N; j += i) {
            t[j] |= flag;
        }
    }

    for(int i = 1;i <= m; i++)
        if(t[i] == 0) {
            cnt ++;
            que.push(i);
        }
    
    cout << que.size() << "\n";
    while(!que.empty()) {
        cout << que.front() << "\n";
        que.pop();
    }

    return 0;
}

B.

二分答案。

一般来说找最大值的最小,最小值的最大一般都是二分答案。

我们二分的是 \(\mathrm{min}\ (\left| x_i-x_j \right|,\left| y_i-y_j \right|)\),假设现在枚举到 \(mid\),那么合法的条件是 \(\mathrm{min}\ (\left| x_i-x_j \right|,\left| y_i-y_j \right|) \geq mid\),即 \(\left| x_i - x_j \right| \geq mid\)\(\left| y_i - y_j \right| \geq mid\)

我们可以把所有点按照 \(x\) 从小到大排序,然后我们可以用双指针维护。

先定位右指针位置,使得 \(x_r - x_l \geq mid\),然后开始移动左指针,并时刻根据 \(x_r - x_l\) 的值移动右指针,这一步就保证了 \(\left| x_i - x_j \right| \geq mid\)

两个指针位置确定时,找到左指针左侧和右指针右侧区间的的 \(y\) 的最大值、最小值,判断二者相减值是否合法。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <climits>
#include <queue>
#include <vector>

using namespace std;

const int N = 1005000;

int n;

struct Point{
    int x,y;
};

bool cmp(const Point &a,const Point &b) {
    return a.x < b.x;
}

vector<Point> a;

bool Check(int mid) {
    int Min = INT_MAX,Max = INT_MIN;
    int flag = 0;
    queue<Point> que;
    // 拿队列模拟指针了 

    for(Point t : a) {
        while(!que.empty()) {
            if(t.x - que.front().x < mid)
                break;
            Max = max(Max,que.front().y);
            Min = min(Min,que.front().y);

            que.pop();
        }
        if(Max - t.y >= mid || t.y - Min >= mid)
            flag = 1;
        que.push(t);
    }
    return flag;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 1,x,y;i <= n; i++) {
        cin >> x >> y;
        a.push_back((Point){x,y});
    }
    
    sort(a.begin(),a.end(),cmp);

    int l = 0,r = 1e9,ans = 0;
    while(l <= r) {
        int mid = (l + r) / 2;
        if(Check(mid)) {
            l = mid + 1;
            ans = mid;
        }
        else
            r = mid - 1;
    }
    cout << ans;
    return 0;
}

剩下的先咕掉吧。

posted @ 2023-07-28 20:17  -白简-  阅读(11)  评论(0编辑  收藏  举报