洛谷7月月赛

本文解析了洛谷7月月赛的ABCD四题,包括DividedPrime、RiverJumping、TrueVegetable和BeautifulPair。涉及质数判断、贪心算法、二分搜索和单调栈等技巧,以及主席树和启发式合并等数据结构应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【LGR-049】洛谷7月月赛

比赛开始一个小时才想起来QwQ,当场写了ABC

#A: P4752 Divided Prime

日常送分题 注意只留一个非1数判他是不是质数

#B: P4753 River Jumping

题意:有跳跃距离下限,求能否经过所有石头恰好一次跳一个来回

贪心

首先距离河岸最近的两个石头必须要能跳上,剩下最多的隔一个跳一个(因为是来回)

#C: P4754 True Vegetable

经典的二分+贪心

二分最小回合数,把所有菜气都减去,然后从左到右贪心加,bit维护

一开始以为r值没用,原来r值保证了答案的单调性

#D: P4755 Beautiful Pair

题意:小D有个数列 \({a}\) ,当一个数对 \((i,j)(i≤j)\) 满足 \(a_i*a_j \le \max(a_i,a_{i+1},…,a_j)\) ,小D认为这个数对是美丽的.请你求出美丽的数对的数量。

单调栈处理出每个值作为最大值的长度a_x [l,r]

然后对于[l,x]中每个值\(a_i\),找[x,r]中满足\(a_j \le \frac{a_x}{a_i}\)的数量,这里可以用主席树实现

但这样复杂度不对,可以选长度短的一段来枚举

复杂度 \(T(n) = T(a) + T(n-a-1) + O(n\log n) = O(n\log^2 n),其中a \le n/2\)

问了一下,可以数归证明。其实就是启发式合并

//D
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5+5;

int n, a[N], mp[N], m;
int st[N], top, l[N], r[N];
void init() {
    for(int i=1; i<=n; i++) {
        int le = i;
        while(top && a[st[top]] <= a[i]) {
            le = l[st[top]];
            r[st[top]] = i-1;
            top--;
        }
        st[++top] = i;
        l[i] = le;
    }
    while(top) r[st[top--]] = n;

    sort(mp+1, mp+1+n);
    m = unique(mp+1, mp+1+n) - mp - 1;
}


struct node {
    int lc, rc, size;
} t[N*18];
int sz, root[N];
void ins(int &x, int l, int r, int p) {
    t[++sz] = t[x]; x = sz;
    t[x].size ++;
    if(l == r) return;
    int mid = (l+r) >> 1;
    if(p <= mid) ins(t[x].lc, l, mid, p);
    else ins(t[x].rc, mid+1, r, p);
}
int que(int x, int y, int l, int r, int ql, int qr) {
    if(mp[r] < ql || qr < mp[l]) return 0;
    if(ql <= mp[l] && mp[r] <= qr) return t[y].size - t[x].size;
    int mid = (l+r) >> 1, ans = 0;
    if(ql <= mp[mid]) ans += que(t[x].lc, t[y].lc, l, mid, ql, qr);
    if(mp[mid] < qr) ans += que(t[x].rc, t[y].rc, mid+1, r, ql, qr);
    return ans;
}

long long ans;
void solve(int l1, int r1, int l2, int r2, int mid) { 
    for(int i=l1; i<=r1; i++) {
        int v = a[mid] / a[i];
        ans += que(root[l2-1], root[r2], 1, m, 1, v);
    }
}
int main() {
    freopen("in", "r", stdin);
    ios::sync_with_stdio(false); cin.tie(); cout.tie();
    cin >> n;
    for(int i=1; i<=n; i++) cin >> a[i], mp[i] = a[i];
    init();
    //for(int i=1; i<=n; i++) printf("hi %d [%d %d]\n", i, l[i], r[i]);
    for(int i=1; i<=n; i++) {
        int x = lower_bound(mp+1, mp+1+m, a[i]) - mp;
        root[i] = root[i-1];
        ins(root[i], 1, m, x);
    }
    for(int i=1; i<=n; i++) {
        int le = l[i], ri = r[i];
        int d1 = i-le, d2 = ri-i;
        //if(d1 == 0 || d2 == 0) continue;

        if(d1 <= d2) solve(le, i, i, ri, i);
        else solve(i, ri, le, i, i);
    }
    cout << ans;
}
//A
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
typedef long long ll;
using namespace std;
const int N = 1e5+5;

int n, m, c[N];
ll a[N], b[N], mp[N];

bool is_p(ll n) {
    for(ll i=2; i*i<=n; i++) if(n%i == 0) return false;
    return true;
}
int main() {
    freopen("in", "r", stdin);
    ios::sync_with_stdio(false);
    int T;
    cin >> T;
    while(T--) {
        mp[0] = 0;
        memset(c, 0, sizeof(c));
        cin >> n >> m;
        for(int i=1; i<=n; i++) cin >> a[i], mp[i] = a[i];
        sort(mp+1, mp+1+n);
        mp[0] = unique(mp+1, mp+1+n) -mp -1;

        for(int i=1; i<=n; i++) {
            int x = lower_bound(mp+1, mp+1+mp[0], a[i]) - mp;
            c[x]++;
        }
        for(int i=1; i<=m; i++) {
            cin >> b[i];
            int x = lower_bound(mp+1, mp+1+mp[0], b[i]) - mp;
            c[x]--;
        }

        int cnt = 0;
        ll ans = 0;
        for(int i=1; i<=mp[0]; i++) if(mp[i] != 1 && c[i]) {
            cnt+=c[i];
            ans = mp[i];
        }
        if(cnt!=1) puts("NO");
        else if(!is_p(ans)) puts("NO");
        else puts("YES");
    }
}
//B
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
typedef long long ll;
using namespace std;
const int N = 1e5+5;


int n, m, s, x[N], vis[N];
vector<int> v;
bool solve() {
    int now = 0;
    if(x[1] < s) return false;
    now = 1; vis[1] = 1; v.push_back(1);
    while(now+2 <= m) {
        if(x[now+2] - x[now] < s) return false;
        vis[now+2] = 1; v.push_back(now+2);
        now += 2;
    }
    if(now == m-1) {
        if(x[m] - x[now] < s) return false;
        vis[m] = 1; v.push_back(m);
        now ++;
    }
    for(int i=m-1; i>=0; i--) if(!vis[i]) {
        if(x[now] - x[i] < s) return false; 
        now = i; v.push_back(i);
    }
    return true;
}
int main() {
    freopen("in", "r", stdin);
    ios::sync_with_stdio(false);

    cin >> n >> m >> s;
    for(int i=1; i<=m; i++) cin >> x[i];
    x[0] = 0; x[++m] = n;
    
    if(!solve()) cout << "NO" << endl;
    else {
        cout << "YES" << endl;
        for(int i=0; i<v.size(); i++) cout << v[i] << " ";
    }
}
//C
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
typedef long long ll;
using namespace std;
const int N = 6e5+5;

int n, m, k, L, a[N], A[N], r[105], w[N], x[N], v[N];

int c[N];
inline void add(int p, int v) { //printf("add %d %d\n", p, v); cout << endl;
    for(; p<=n; p+=p&-p) c[p] += v;
}
inline int sum(int p) {
    int ans = 0;
    for(; p; p-=p&-p) ans += c[p];
    return ans;
}
inline void add(int l, int r, int v) {
    add(l, v);
    add(r+1, -v);
}

bool check(int mid) { //printf("check %d\n", mid);
    for(int i=1; i<=n; i++) a[i] = A[i];
    for(int p = 1; p <= m && w[p] <= mid; p++) a[x[p]] -= v[p];

    memset(c, 0, sizeof(c));
    for(int i=n; i>=1; i--) a[i] -= a[i-1];
    for(int i=1; i<=n; i++) add(i, a[i]);
    

    int ans = 0;
    for(int i=1; i<=n; i++) {
        int x = sum(i); //printf("hi %d %d\n", i, x);
        if(x < 1) add(i, i+k-1, 1-x), ans += 1-x; 
    } 
    //printf("ans %d\n", ans);
    return ans <= mid;
}
int main() {
    freopen("in", "r", stdin);
    ios::sync_with_stdio(false);

    cin >> n >> m >> k >> L;
    for(int i=1; i<=n; i++) cin >> A[i];
    for(int i=1; i<=L; i++) cin >> r[i];
    for(int i=1; i<=m; i++) cin >> w[i] >> x[i] >> v[i];
    int l = 0, r = 1e8, ans = 0;
    while(l <= r) { //printf("erfen %d %d\n", l, r);
        int mid = (l+r) >> 1;
        if(check(mid)) ans = mid, r = mid-1;
        else l = mid+1;
    }
    cout << ans;
}
<think>我们被要求解决一个编程竞题目:计算三维物体的三视图投影面积。题目来源于洛谷[语言 202307] Three-View Projection。 问题描述:给定一个 n x n 的网格,每个网格位置 (i, j) 有一个非负整数 v(i, j) 表示该位置堆叠的立方体个数。我们需要计算三个投影(俯视图、前视图、左视图)的面积之和。 三视图投影的定义: 1. 俯视图:从顶部往下看,我们能看到每个位置至少有一个立方体,则面积为非零位置的个数(即如果v(i,j)>0,则贡献1,否则0)。 2. 前视图:从前面看(沿y轴方向),对于每一列j,我们取这一列中每一行i的最大值,即 max_i v(i,j)。然后,前视图的面积就是所有列的最大值之和。 3. 左视图:从左侧看(沿x轴方向),对于每一行i,我们取这一行中每一列j的最大值,即 max_j v(i,j)。然后,左视图的面积就是所有行的最大值之和。 因此,总面积 = 俯视图面积 + 前视图面积 + 左视图面积。 具体步骤: 1. 俯视图:遍历网格,统计v(i,j)>0的个数。 2. 前视图:对于每一列j,计算该列的最大值,然后把这些最大值相加。 3. 左视图:对于每一行i,计算该行的最大值,然后把这些最大值相加。 注意:题目中网格大小为n x n。 示例:输入[[1,2],[3,4]] 俯视图:所有位置都大于0,所以面积=4(但实际是2x2网格,有4个位置,每个位置都非零)?但是注意,题目中俯视图的定义:在俯视图中,每个位置只要有立方体,就会占据一个单位面积(无论堆叠多高,俯视图只看到一个面)。所以俯视图面积就是非零位置数,即4。 前视图:列0的最大值为max(1,3)=3,列1的最大值为max(2,4)=4,所以前视图面积=3+4=7。 左视图:行0的最大值为max(1,2)=2,行1的最大值为max(3,4)=4,所以左视图面积=2+4=6。 总面积=4+7+6=17。 因此,示例输入[[1,2],[3,4]],输出17。 实现代码(Python): 我们可以直接按照上述步骤实现。 注意:题目输入是一个二维列表,大小为n x n。 代码步骤: 1. 初始化俯视图面积(top)为0,前视图面积(front)为0,左视图面积(side)为0。 2. 遍历每一行i: 计算当前行的最大值,加到左视图面积中。 遍历每一列j: 如果grid[i][j] > 0,则俯视图面积加1。 3. 遍历每一列j: 初始化当前列的最大值为0。 遍历每一行i,更新当前列的最大值。 将当前列的最大值加到前视图面积中。 4. 总面积 = top + front + side 但是注意:在步骤2中,我们可以在遍历行和列的同时记录每一列的最大值,这样只需要两个循环。 优化:我们可以一次遍历完成三个面积的计算。 具体: top: 统计非零元素个数 side: 每行的最大值之和 front: 每列的最大值之和 我们可以: top = 0 side = [0] * n # 这里不需要提前记录每行的最大值,但我们可以直接累加每行最大值到side_sum,不过我们也可以先计算每行最大值 front = [0] * n # 同样,记录每列的最大值 但是注意,我们可以: top: 在遍历每个元素时,如果grid[i][j]>0,则top++。 side: 在遍历每一行时,记录当前行的最大值,然后累加。 front: 在遍历每一行时,更新每列的最大值(遍历完所有行后,每列的最大值就得到了)。 因此,我们可以: top = 0 side_sum = 0 front_max = [0] * n # 初始化每列的最大值为0 for i in range(n): row_max = 0 for j in range(n): if grid[i][j] > 0: top += 1 if grid[i][j] > row_max: row_max = grid[i][j] if grid[i][j] > front_max[j]: front_max[j] = grid[i][j] side_sum += row_max front_sum = sum(front_max) total = top + side_sum + front_sum 但是注意:在初始化front_max时,由于立方体个数非负,我们可以初始化为0。但是当n=0时,需要特殊处理。 然而,题目没有说n=0的情况,一般n>=1。 但是,我们也可以不用数组记录front_max,而是在遍历完后再计算列最大值?但这样就需要额外循环。所以我们采用上述方法。 另一种方法:我们可以先遍历每一列,计算列最大值,但这样需要两个循环。而上述方法是一个二重循环。 我们采用一个二重循环完成。 代码实现: 注意:题目中网格是n x n,所以我们假设grid是一个n行n列的二维列表。 代码示例: 输入:grid = [[1,2],[3,4]] 输出:17 测试另一个例子:grid = [[2]] -> top: 1 (因为2>0) side: 行0的最大值=2 -> side_sum=2 front: 列0的最大值=2 -> front_sum=2 total=1+2+2=5 再测试:grid = [[0,2],[3,0]] top: 位置(0,0)=0 -> 不计,位置(0,1)=2 -> 计1,位置(1,0)=3 -> 计1,位置(1,1)=0 -> 不计 -> top=2 side: 行0最大值=2,行1最大值=3 -> side_sum=5 front: 列0最大值=max(0,3)=3,列1最大值=max(2,0)=2 -> front_sum=5 total=2+5+5=12 因此,编写函数: 注意:题目要求是竞题,所以我们需要考虑效率,但这里n最大不会太大(一般1000以内),所以O(n^2)可以接受。 代码实现如下:</think>### 三视图投影面积问题解析 #### 问题描述 给定一个 $n \times n$ 网格,每个位置 $(i,j)$ 有非负整数 $v_{ij}$ 表示立方体堆叠数量。计算三维物体的三视图投影总面积: - **俯视图**:从顶部观察,面积为非零位置的数量 - **前视图**:从正面观察,面积为每列最大值的和 - **左视图**:从左侧观察,面积为每行最大值的和 总面积公式: $$ S_{\text{total}} = \underbrace{\sum_{i=0}^{n-1} \sum_{j=0}^{n-1} [v_{ij} > 0]}_{\text{俯视图}} + \underbrace{\sum_{j=0}^{n-1} \max_{i} v_{ij}}_{\text{前视图}} + \underbrace{\sum_{i=0}^{n-1} \max_{j} v_{ij}}_{\text{左视图}} $$ 其中 $[P]$ 是 Iverson 括号(条件成立时为 1,否则为 0)。 #### 示例分析 输入:$[[1,2],[3,4]]$ 1. **俯视图**:所有位置非零 → $4$ 2. **前视图**:列最大值 $[\max(1,3), \max(2,4)] = [3,4]$ → $3+4=7$ 3. **左视图**:行最大值 $[\max(1,2), \max(3,4)] = [2,4]$ → $2+4=6$ 总面积:$4 + 7 + 6 = 17$[^1] #### 算法实现 ```python def projection_area(grid): n = len(grid) top = 0 front = [0] * n # 存储每列最大值 side = 0 for i in range(n): row_max = 0 for j in range(n): # 计算俯视图(非零计数) if grid[i][j] > 0: top += 1 # 更新行最大值 if grid[i][j] > row_max: row_max = grid[i][j] # 更新列最大值 if grid[i][j] > front[j]: front[j] = grid[i][j] side += row_max # 累加行最大值 return top + side + sum(front) # 测试 print(projection_area([[1,2],[3,4]])) # 输出 17 print(projection_area([[2]])) # 输出 5 (1+2+2) print(projection_area([[0,2],[3,0]])) # 输出 12 (2+5+5) ``` #### 复杂度分析 - **时间复杂度**:$O(n^2)$,遍历 $n \times n$ 网格一次 - **空间复杂度**:$O(n)$,存储列最大值数组 #### 关键点说明 1. **俯视图**:只需统计 $v_{ij} > 0$ 的位置数量 2. **行列最值**:在遍历过程中同步计算: - 行最大值即时累加 - 列最大值存储在数组中最后求和 3. **边界处理**:当 $n=0$ 时返回 0(代码未展示,可添加)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值