AtCoder Beginner Contest 395 题解ABCDEF

A - Strictly Increasing?

题意:给你一个正整数 N 和一个长度为 N 的正整数序列 A=(A1,A2,…,AN)。请判断 A 是否严格递增

思路:模拟即可

// Code Start Here	
	int n;
	cin >> n;
	vector<int> a(n);
	bool f = true;
	for(auto &it : a)cin >> it;
	for(int i = 0;i<n-1;i++){
		if(a[i] >= a[i+1])f = false;
	}
	if(f)cout <<"Yes\n";
	else cout <<"No\n";

B:

题意:

给你一个正整数 N 。

考虑一个 N×N 网格。让 (i,j)表示从上往下第 i 行,从左往上第 j 列的单元格。最初,没有单元格被着色。

然后,依次对 i=1,2,…,N进行以下操作:

  • 让 j=N+1−i
  • 如果是 i≤j,则在左上角单元格为 (i,i)、右下角单元格为 (j,j) 的矩形区域填充黑色(如果 i为奇数),如果 i 为偶数,则填充白色。如果某些单元格已经着色,则覆盖它们的颜色。
  • 如果是 i>j,则不做任何操作。

经过所有这些操作后,可以证明没有未着色的单元格。确定每个单元格的最终颜色。

思路:N < 50  模拟即可

// Code Start Here	
	int n;
	cin >> n;
	vector<vector<char>> g(n+1,vector<char>(n+1));
	for(int i = 1;i<=n;i++){
		int j = n + 1 - i;
		if(i <= j){
			for(int now_i = i;now_i<=j;now_i++){
				for(int now_j = i;now_j <=j;now_j++){
					if(i & 1)g[now_i][now_j] = '#';
					else g[now_i][now_j] = '.';
				}
			}
		}
	}
	for(int i = 1;i<=n;i++){
		for(int j = 1;j<=n;j++){
			cout << g[i][j];
		}
		cout << endl;
	}

C

题意:

给你一个正整数 N 和一个长度为 N 的整数序列 A=(A1​,A2​,…,AN​) 。

请判断 A 是否存在一个非空(连续)的子数组,它有一个重复值,多次出现在 A 中。如果存在这样的子数组,求最短的子数组的长度。

思路:由题意有,对于一个ai,找下一个理他最近的ai,注意到N < 1e5所以双重遍历不理想,思考把相同的元素融入到一个vector里面,每次看vector里面两个相邻的元素即可,转化到On左右

// Code Start Here	
	int n;
	cin >> n;
	map<int,vector<int>> g;
	vector<int> a(n+1);
	for(int i = 1;i<=n;i++)cin >> a[i];
	for(int i = 1;i<=n;i++){
		g[a[i]].push_back(i);
	}
	int ans = INT_MAX;
	for(auto [val,now] : g){
		for(int i = 0;i<(int)(now.size()) - 1;i++){
			ans = min(now[i+1] - now[i] , ans);
		}
	}
	if(ans == INT_MAX)cout <<"-1\n";
	else cout << ans + 1 <<endl;

D - Pigeon Swap

题意:

有 N 只标有 1,2,…,N 的鸽子和 N 个标有 1,2,…,N 的鸽巢。

最初,鸽子 i(1≤i≤N) 在巢 ii 中。

您将对这些鸽子执行 Q 操作。

操作有三种:

  • 类型 1 :给你整数 a 和 b 。 (1≤a≤N,1≤b≤N).将鸽子 a 从当前巢中取出,移到巢 b 中。
  • 输入 2 :给你整数 a 和 b 。 (1≤a<b≤N) .将巢 a 中的所有鸽子移动到巢 b ,并将巢 b 中的所有鸽子移动到巢 a 。这两次移动同时进行。
  • 输入 3 :给你一个整数 a 。 (1≤a≤N) .鸽子 a 报告它当前所在鸽巢的标签。

按照给出的操作顺序打印每个类型 3 操作的结果。

思路:简单来想肯定能想到对于每个巢穴开一个set模拟,然后存储一下每个鸽子的巢穴pos,然后根据题意模拟一下即可

但是这样显然有一个问题,在对于2操作进行模拟的时候,要把两个巢穴的鸽子全部清空,然后互相copy,当N过大时,两个互相copy的过程能达到On^2,显然超时

// TLE version
// Code Start Here	
	int n , q;
	cin >> n >> q;
	vector<int> pos(n+1,-1);
	for(int i = 1;i<=n;i++)pos[i] = i;
	vector<set<int>> g(n+1);
	for(int i = 1;i<=n;i++)g[i].insert(i);
	while(q--){
		int op;
		cin >> op;
		if(op == 1){
			int a , b;
			cin >> a >> b;
			g[pos[a]].erase(a);
			pos[a] = b;
			g[pos[a]].insert(a);
		}
		else if(op == 2){
			int a , b;
			cin >> a >> b;
			vector<int> now_a;
			vector<int> now_b;
			for(auto it = g[a].begin() ; it != g[a].end() ; it++){
				now_a.push_back(*it);
			}
			for(auto it = g[b].begin() ; it != g[b].end() ; it++){
				now_b.push_back(*it);
			}
			g[a].clear();
			g[b].clear();
			for(auto i : now_a){
				pos[i] = b;
				g[pos[i]].insert(i);
			}
			for(auto i : now_b){
				pos[i] = a;
				g[pos[i]].insert(i);
			}
		}
		else{
			int a;
			cin >> a;
			cout << pos[a] << endl;
		}
	}

考虑一下优化方式,不涉及的巢穴中去鸽子,2操作的实质就是把两个巢穴的标签编号改变一下,所以开一个数组记录一下当前巢穴的“真”编号,然后模拟即可

// Code Start Here	
	int n , q;
	cin >> n >> q;
	vector<int> pos(n+1,-1);
	vector<int> nest(n+1,-1);
	vector<int> head(n+1,-1);
	for(int i = 1;i<=n;i++){
		pos[i] = i;
		head[i] = i;
		nest[head[i]] = i;
	}
	while(q--){
		int op;
		cin >> op;
		if(op == 1){
			int a , b;
			cin >> a >> b;
			pos[a] = head[b];
		}
		else if(op == 2){
			int a , b;
			cin >> a >> b;
			int temp = nest[head[a]];
			nest[head[a]] = nest[head[b]];
			nest[head[b]] = temp;
			temp = head[a];
			head[a] = head[b];
			head[b] = temp;
		}
		else{
			int a;
			cin >> a;
			cout << nest[pos[a]] << endl;
		}
	}

E - Flip Edge

题意:在一个有向图中,我们从节点1出发,要通过两种操作到达节点N。操作一是沿着当前的有向边移动,消耗1的代价。操作二是翻转所有边的方向,消耗X的代价。我们的目标是找到到达节点N的最小总代价

思路:

我们发现对于每个状态有两种可能:

1.沿着原始边移动

2.沿着反转边移动

记录一下两种情况下的最小值,然后跑一遍dijk即可

// Code Start Here	
	int n , m , x;
	cin >> n >> m >> x;
	vector<vector<int>> ore(n+1);
	vector<vector<int>> inv(n+1);
	vector<vector<int>> dis(n+1,vector<int>(2,LLONG_MAX));
	for(int i = 1;i<=m;i++){
		int u , v;
		cin >> u >> v;
		ore[u].push_back(v);
		inv[v].push_back(u);
	}
	dis[1][0] = 0;
	priority_queue<pair<int,pair<int,int>>,vector<pair<int,pair<int,int>>> ,greater<pair<int,pair<int,int>>>> pq;
	pq.push({0,{1,0}});
	while(!pq.empty()){
		auto cur = pq.top();
		pq.pop();
		int now_val = cur.first;
		int u = cur.second.first;
		int op = cur.second.second;
		if(u == n){
			cout << now_val << endl;
			return 0;
		}
		if(op == 0){
			for(int v : ore[u]){
				if(dis[v][op] > now_val + 1){
					dis[v][op] = now_val + 1;
					pq.push({dis[v][op],{v , 0}});
				}
			}
		}
		else{
			for(int v : inv[u]){
				if(dis[v][op] > now_val + 1){
					dis[v][op] = now_val + 1;
					pq.push({dis[v][op],{v,1}});
				}
			}
		}
		if(dis[u][1-op] > now_val + x){
			dis[u][1-op] = now_val + x;
			pq.push({dis[u][1-op],{u,1-op}});
		}
	}

F - Smooth Occlusion

题意:

高桥有 2N 颗牙齿:上牙有 N 颗,下牙有 N 颗。

左起第 i 颗上牙( 1≤i≤N)的长度是 Ui ,左起第 i 颗下牙( 1≤i≤N )的长度是 Di​ 。

如果同时满足以下两个条件,那么他的牙齿就可以说是 "合得很好":

  • 存在一个整数 H ,使得每个整数 i 的 Ui+Di=H 与 1≤i≤N .
  • 每个整数 i 都有 ∣Ui−Ui+1∣≤X 与 1≤i<N 。

他可以多次进行下面的运算:

  • 支付 1 日元使用磨齿机,选择一个长度为正数的牙齿,并将其长度减少 1 。

不得使用其他方法改变牙齿的长度。

求为了使牙齿整齐排列,他至少需要支付的总金额。

思路:注意到H 必须满足 Ui+Di=H对于所有 i,所以 H 不能超过任何一对牙齿的总长度,所以需要确定最大可能的H。然后记录一下后续牙齿与U_i-1的范围计算一下新的范围,最后计算一下最小成本。

// Code Start Here	
	int n , x;
	cin >> n >> x;
	vector<int> u(n),d(n),s(n);
	int sum = 0;
	for(int i = 0;i<n;i++){
		cin >> u[i] >> d[i];
		s[i] = u[i] + d[i];
		sum += s[i];
	}
	int max_val = *min_element(s.begin(),s.end());
	auto check = [&](int h)->bool{
		int low = max(0 * 1LL , h - d[0]);
		int high = min(h , u[0]);
		if( low > high)return false;
		for(int i = 1;i<n;i++){
			int l = max(0 * 1LL , h - d[i]);
			int r = min(h , u[i]);
			if(l > r)return false;
			int nl = max(l , low - x);
			int nr = min(r , high + x);
			if(nl > nr)return false;
			low = nl;
			high = nr;
		}
		return true;
	};
	int l = 0 , r = max_val;
	int ans = 0;
	while(l <= r){
		int mid = ( l + r ) / 2;
		if(check(mid)){
			ans = mid;
			l = mid + 1;
		}
		else r = mid - 1;
	}
	cout << sum - ans * n << endl;

AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值