[每日随题1] 模拟 - 差分数组 - 树状数组

整体概述

  • 难度:800 -> 1200 -> 1600

1742B.Increasing

  • 标签:模拟

  • 前置知识:无

  • 难度:Div.4.B 800

题目描述:

image

输入格式:

image

输出格式:

image

样例输入:

3
4
1 1 1 1
5
8 7 1 3 4
1
5

样例输出:

NO
YES
YES

解题思路:

  • 要求最后数组严格递增,即不存在相同元素,即可满足题意。

  • 那么我们把数组排个序,相邻元素不相同,即不存在相同元素。

完整代码

#include<bits/stdc++.h>
#define Size(x) ((int)(x).size())
#define int long long
using namespace std;
const int N = 100+5;
int a[N];
inline string solve(){ 
	int n; cin >> n;
	for(int i=1;i<=n;i++) cin >> a[i];
	sort(a+1,a+1+n);
	for(int i=2;i<=n;i++) if(a[i] == a[i-1]) return "No";
	return "Yes";
} 
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int T; cin >> T;
	while(T--) cout << solve() << '\n';
	return 0;
}

892B.Wrath

  • 标签:差分数组

  • 前置知识:无

  • 难度:Div.2.B 1200

题目描述:

image

输入格式:

image

输出格式:

image

样例输入:

4
0 1 0 10
2
0 0
10
1 1 3 0 0 0 2 1 0 3

样例输出:

1
2
3

解题思路:

  • 我们注意到所有人同时行动,杀死一个范围内的所有人。

  • 那么考虑统计每个人被打到的次数,即 \(n\) 次 区间修改,每次将一个范围上所有人 \(+1\)。最后统一查询,查询有多少个人没有被打到。用差分数组即可。

完整代码

#include<iostream>
#define Size(x) ((int)(x).size())
#define int long long
using namespace std;
const int N = 1e6+5;
int d[N];
inline void solve(){
	int n; cin >> n;
	for(int i=1,x;i<=n;i++){
		cin >> x;
		d[max(0ll,i-x)] += 1, d[i] -= 1;
	}
	int res = 0;
	for(int i=1;i<=n;i++){
		d[i] += d[i-1];
		if(!d[i]) ++res;
	}
	cout << res;
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int T; T = 1;
	while(T--) solve();
	return 0;
}

830B.Cards Sorting

  • 标签:树状数组

  • 前置知识:STL-set

  • 难度:Div.1.B 1600

题目描述:

image

输入格式:

image

输出格式:

image

样例输入:

4
6 3 1 2
1
1000
7
3 3 3 3 3 3 3

样例输出:

7
1
7

解题思路:

  • 模拟操作的过程发现,最耗时的是 将牌放到牌堆底部,所以我们不能真的每取出一张就一张张移动牌的位置。

  • 我们发现整个操作过程牌的相对位置保持不变,那么我们用一个指针 \(p\) 记录下上一张被取走的牌的位置,再记录下每一张牌是否被取走了。那么此时的牌堆顶的位置,就是 \(p\) 下方第一张存活的牌。

    之后考虑模拟整个取牌的过程,从小到大依次取每一个数值。我们需要知道在指针 \(p\) 下方第一张数值为 \(x\) 的牌的位置,那么可以考虑记录下所有数值为 \(x\) 的牌的位置,进行二分。

    如果 \(p\) 后方没有找到,说明 \(x\) 牌在 \(p\) 的前面,那么我们在从头找到第一张即可。

  • 于是我们便得到了下一张会被取走的牌的位置,而这个过程中需要经过的牌的数量,便是从 \(p\) 到牌 \(x\) 中所有存活的牌的张数,我们记存活为 \(1\),被取走为 \(0\),可以用树状数组快速查询一个范围上有多少个 \(1\)

    那么每取走一张牌加上正确的张数,再修改 \(p\) 的位置,模拟一遍即可。

  • 复杂度 \(O(n\times log_2^n)\)

完整代码

#include<bits/stdc++.h>
#pragma optimize(2)
#define int long long
using namespace std;
const int N = 1e5+5;
int n, a[N], tr[N];
set<int> idx[N];
inline void add(int x,int v){
	for(int i=x;i<=n;i+=i&-i) tr[i]+=v;
}
inline int sum(int l,int r){
	int res = 0;
	if(l == 0) l = 1;
	for(int i=r;i;i&=i-1) res += tr[i];
	for(int i=l-1;i;i&=i-1) res -= tr[i];
	return res;
}
inline void solve(){ 
	cin >> n;
	for(int i=1;i<=n;i++){
		cin >> a[i]; 
		idx[a[i]].insert(i);
		add(i,1);
	} 
	sort(a+1,a+1+n);
	int p = 0,res = 0;
	for(int i=1;i<=n;i++){
		auto it = idx[a[i]].upper_bound(p);
		if(it == idx[a[i]].end()){
			res += sum(p,n);
			p = 0, it = idx[a[i]].upper_bound(p);
		}
		res += sum(p,*it);
		p = *it;
		add(p,-1);
	}
	cout << res;
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int T; T = 1;
	while(T--) solve();
	return 0;
}

posted @ 2025-07-08 01:55  浅叶梦缘  阅读(13)  评论(0)    收藏  举报