Codeforces Round #546 (Div. 2) 题解

博客包含几道算法题,如给定n个区间和位置k求未完全遍历区间,有n个井盖求捡完全部金币最小步数等。针对不同题目给出思路,如数据范围小直接暴力,考虑转置特性判断矩阵能否转换等,部分题还给出具体解法和复杂度分析。

A. Nastya Is Reading a Book
题意: n个区间,每个区间范围不超过100,n不超过100。给一个位置k,1-(k-1)是遍历过的位置,求没有完全遍历完的区间。
k处于区间中时,表示区间没有遍历完。
思路:数据范围小,直接暴力

#include <bits/stdc++.h>
using namespace std;
pair <int, int> p[102];
int main(){
    int n, k, ans = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d%d", &p[i].first, &p[i].second);
    scanf("%d", &k);
    for(int i = 1; i <= n; i++){
        if(p[i].second < k){
            ans++;
        }
    }
    printf("%d\n", n-ans);
}

B. Nastya Is Playing Computer Games
题意:有n个井盖,每个井盖上有一个小石头。给出n和k,k表示刚开始在第k个井盖上方。有三种操作,左右移动,扔石头到任意一个井盖,下到井盖里拿金币。只有井盖上没有石头才能下井盖。求捡完全部金币的最小步数。
思路:规律题,我们考虑一下当前位置和相邻的任意一个位置,要想拿到这2个井盖的金币一共需要6次。其余的井盖需要(n-2)*3个步骤,还需要考虑这个人是从哪个方向走导致多出来的步数,两个取小即可。

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    int ans = n * 3 + min(k - 1, n - k);
    printf("%d\n", ans);
    return 0;
}

C:题意,给了俩个矩阵,你可以无数次对A矩阵的子矩阵进行转置操作,问A是否可以变为B?
思路:考虑一下转置的特性,发现不在同一对角线的元素是无法交换位置的。我们直接对主对角线进行求和判断即可。

#include <bits/stdc++.h>
using namespace std;
int a[505][505], b[505][505];
map <int, int> mp[1010];
int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            scanf("%d", &a[i][j]);
            mp[i+j-1][a[i][j]]++;
        }
    }
    bool flag = 1;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            scanf("%d", &b[i][j]);
            if(mp[i+j-1][b[i][j]] <= 0){
                flag = 0;
                break;
            }
            mp[i+j-1][b[i][j]]--;
        }
    }
    if(flag){
        puts("YES");
    }else{
        puts("NO");
    }
}

D:题意:给一个序列和一组交换序列(a,b),当且仅当a在b的前面(不允许有间隔),这两个数才能交换,问最后一个数最多能移动多少个位置。
解法:不会,阅读题解,用数组cnt[x]记录数x后面可以与其交换的数的数目,当这个数目刚好等于这个数和最后一个数的距离时,肯定有办法能把最后一个数换到x的位置,然后需要注意的是要一直更新最后一个数的位置,它的位置就是n-ans(即数列长度-已经移动的次数)。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+10;
int p[maxn], cnt[maxn];
vector <int> v[maxn];
int main(){
    int n, m, x, y;
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++){
        scanf("%d", &p[i]);
    }
    for(int i = 1; i <= m; i++){
        scanf("%d %d", &x, &y);
        v[y].push_back(x);
    }
    for(int i = 0; i < v[p[n]].size(); i++){
        cnt[v[p[n]][i]]++;
    }
    int ans = 0;
    for(int i = n - 1; i >= 1; i--){
        if(n - i - ans == cnt[p[i]]){
            ans++;
        }else{
            for(int j = 0; j < v[p[i]].size(); j++){
                cnt[v[p[i]][j]]++;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

E. Nastya Hasn’t Written a Legend
题意:有一个数组a和一个数组k,数组a一直保持一个性质:a[i + 1] >= a[i] + k[i]。有两种操作:1,给某个元素加上x,但是加上之后要保持数组a的性质。比如a[i]加上x之后,a[i + 1]<a[i] + k[i],那么a[i + 1]就变成a[i] + k[i],否则不变。同理,若a[i + 2]小于了现在的a[i + 1] + k[i + 1],那么a[i + 2]也变成a[i + 1] + k[i + 1],一直保持这个性质。第二章操作,询问数组a的区间[l, r]的区间和。
题解:https://www.cnblogs.com/pkgunboat/p/10527569.html
题解:来自上面的博主,容易发现,假设更改了x位置之后,恰好到位置y不需要更改元素,那么及其之后的位置肯定就不用更改了。所以,在查找这个位置的时候,我们可以二分查找。找到之后,对区间[x, y]进行操作。我们可以发现,区间[x, y]的数有规律。假设x位置的数是a[x],那么a[x + 1]是a[x] + k[x], a[x + 2]是a[x] + k[x + 1] + k[x + 2],以此类推。这个区间的和可以分为2部分:[y - x + 1]个a[x],和关于k的部分。可以发现,k[x]出现了(r - l + 1)次,k[x + 1]出现了(r - l)次,以此类推。那么怎么O(1)获得这个东西呢?我们先预处理k数组的前缀和(假设前缀和数组是b),那么我们再求b的前缀和(假设是数组c),那么c[i]就是出现了i次k[1],i - 1次k[2],以此类推。那么区间[x, y]中关于k的和就可以得出了:c[y] - c[x - 1] - [y - x + 1] * b[x]。前面c[y] - c[x - 1]可以O(1)算出,而后面的部分比较麻烦。怎么维护后面[y - x + 1] * b[x]这个部分呢?我们发现a[x]正好出现[y - x + 1]次,这样我们可以把a[x] - b[x]用一个懒标记维护,下放标记的时候区间的和为c[y] - c[x - 1] + (r - l + 1) * val (val是a[x] - b[x])。
这个算法的复杂度是nlog(n),我当时在想分块是不是可以维护,但估计会T。太菜了,完全没有状态了,只能看看题解写写,告辞。

#include <stdio.h>
#include <iostream>
using namespace std;
const int maxn = 1e6 + 10;
const long long INF = 1e18;
long long a[maxn], b[maxn], c[maxn];
struct node {
	int l, r;
	long long sum, tag;
}Tree[maxn << 2];

void push_up(int rt) {
	Tree[rt].sum = Tree[rt * 2].sum + Tree[rt * 2 + 1].sum;
}

void push_down(int rt) {
	if (Tree[rt].tag != -INF) {
		Tree[rt * 2].sum = (Tree[rt * 2].r - Tree[rt * 2].l + 1) * Tree[rt].tag + (c[Tree[rt * 2].r] - c[Tree[rt * 2].l - 1]);
		Tree[rt * 2].tag = Tree[rt].tag;
	}
	if (Tree[rt].tag != -INF) {
		Tree[rt * 2 + 1].sum = (Tree[rt * 2 + 1].r - Tree[rt * 2 + 1].l + 1) * Tree[rt].tag + (c[Tree[rt * 2 + 1].r] - c[Tree[rt * 2 + 1].l - 1]);
		Tree[rt * 2 + 1].tag = Tree[rt].tag;
	}
	Tree[rt].tag = -INF;
}

void build(int l, int r, int rt) {
	Tree[rt].l = l, Tree[rt].r = r;
	Tree[rt].tag = -INF;
	if (l == r) {
		Tree[rt].sum = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, rt * 2);
	build(mid + 1, r, rt * 2 + 1);
	push_up(rt);
}

void update(int L, int R, int l, int r, int rt, long long val) {
	if (L <= l && r <= R) {
		if (val != -INF) {
			Tree[rt].sum = (Tree[rt].r - Tree[rt].l + 1) * val + (c[Tree[rt].r] - c[Tree[rt].l - 1]);
			Tree[rt].tag = val;
		}
		return;
	}
	push_down(rt);
	int mid = (l + r) >> 1;
	if (L <= mid) update(L, R, l, mid, rt * 2, val);
	if (mid < R) update(L, R, mid + 1, r, rt * 2 + 1, val);
	push_up(rt);
}

long long query(int L, int R, int l, int r, int rt) {
	if (L <= l && r <= R) {
		return Tree[rt].sum;
	}
	push_down(rt);
	int mid = (l + r) >> 1;
	long long ans = 0;
	if (L <= mid) ans += query(L, R, l, mid, rt * 2);
	if (mid < R) ans += query(L, R, mid + 1, r, rt * 2 + 1);
	return ans;
}

int main() {
	int n, q;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
	}
	for (int i = 2; i <= n; i++) {
		scanf("%lld", &b[i]);
		b[i] += b[i - 1];
	}
	for (int i = 2; i <= n; i++) {
		c[i] = c[i - 1] + b[i];
	}
	build(1, n, 1);
	scanf("%d", &q);
	char op[10];
	int x, y;
	while (q--) {
		scanf("%s%d%d", op + 1, &x, &y);
		if (op[1] == '+') {
			int l = x;
			int r = n;
			long long temp = query(x, x, 1, n, 1);
			while (l < r) {
				//int mid = (l + r) >> 1;
				int mid = (l + r + 1) >> 1;
				long long temp1 = query(mid, mid, 1, n, 1);
				if ((temp + y + b[mid] - b[x]) > temp1) {
					l = mid;
				}
				else {
					r = mid - 1;
				}
			}
			//printf("************%d %d %d*********\n", x, l, temp + y - b[x]);
			update(x, l, 1, n, 1, temp + y - b[x]);
		}
		else {
			printf("%lld\n", query(x, y, 1, n, 1));
		}
	}
	//system("pause");
	return 0;
}
【SCI复现】含可再生能源与储能的区域微电网最优运行:应对不确定性的解鲁棒性与非预见性研究(Matlab代码实现)内容概要:本文围绕含可再生能源与储能的区域微电网最优运行展开研究,重点探讨应对不确定性的解鲁棒性与非预见性策略,通过Matlab代码实现SCI论文复现。研究涵盖多阶段鲁棒调度模型、机会约束规划、需求响应机制及储能系统优化配置,结合风电、光伏等可再生能源出力的不确定性建模,提出兼顾系统经济性与鲁棒性的优化运行方案。文中详细展示了模型构建、算法设计(如C&CG算法、大M法)及仿真验证全过程,适用于微电网能量管理、电力系统优化调度等领域的科研与工程实践。; 适合人群:具备一定电力系统、优化理论和Matlab编程基础的研究生、科研人员及从事微电网、能源管理相关工作的工程技术人员。; 使用场景及目标:①复现SCI级微电网鲁棒优化研究成果,掌握应对风光负荷不确定性的建模与求解方法;②深入理解两阶段鲁棒优化、分布鲁棒优化、机会约束规划等先进优化方法在能源系统中的实际应用;③为撰写高水平学术论文或开展相关课题研究提供代码参考和技术支持。; 阅读建议:建议读者结合文档提供的Matlab代码逐模块学习,重点关注不确定性建模、鲁棒优化模型构建与求解流程,并尝试在不同场景下调试与扩展代码,以深化对微电网优化运行机制的理解。
个人防护装备实例分割数据集 一、基础信息 数据集名称:个人防护装备实例分割数据集 图片数量: 训练集:4,524张图片 分类类别: - Gloves(手套):工作人员佩戴的手部防护装备。 - Helmet(安全帽):头部防护装备。 - No-Gloves(未戴手套):未佩戴手部防护的状态。 - No-Helmet(未戴安全帽):未佩戴头部防护的状态。 - No-Shoes(未穿安全鞋):未佩戴足部防护的状态。 - No-Vest(未穿安全背心):未佩戴身体防护的状态。 - Shoes(安全鞋):足部防护装备。 - Vest(安全背心):身体防护装备。 标注格式:YOLO格式,包含实例分割的多边形坐标和类别标签,适用于实例分割任务。 数据格式:来源于实际场景图像,适用于计算机视觉模型训练。 二、适用场景 工作场所安全监控系统开发:数据集支持实例分割任务,帮助构建能够自动识别工作人员个人防护装备穿戴状态的AI模型,提升工作环境安全性。 建筑与工业安全检查:集成至监控系统,实时检测PPE穿戴情况,预防安全事故,确保合规性。 学术研究与创新:支持计算机视觉在职业安全领域的应用研究,促进AI与安全工程的结合。 培训与教育:可用于安全培训课程,演示PPE识别技术,增强员工安全意识。 三、数据集优势 精准标注与多样性:每个实例均用多边形精确标注,确保分割边界准确;覆盖多种PPE物品及未穿戴状态,增加模型鲁棒性。 场景丰富:数据来源于多样环境,提升模型在不同场景下的泛化能力。 任务适配性强:标注兼容主流深度学习框架(如YOLO),可直接用于实例分割模型开发,支持目标检测和分割任务。 实用价值高:专注于工作场所安全,为自动化的PPE检测提供可靠数据支撑,有助于减少工伤事故。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值