CF2149题解

A. Be Positive

code
#include<bits/stdc++.h>
using namespace std;
const int NN = 1e4;
int T;
int n,a[NN];
int cnt1,cnt2;
void solve(){
	cnt1 = cnt2 = 0;
	cin >> n;
	for(int i = 1; i <= n; ++i){
		cin >> a[i];
		if(a[i] == -1) ++cnt1;
		else if(a[i] == 0) ++cnt2;
	}
	cout << cnt2 + (cnt1&1) * 2 << '\n';
} 
int main(){
	cin >> T;
	while(T--){
		solve();
	}
} 

B. Unconventional Pairs

code
#include<bits/stdc++.h>
using namespace std;
const int NN = 2e5 + 8;
int t,n;
int a[NN];
void solve(){
	cin >> n;
	for(int i = 1; i <= n; ++i){
		cin >> a[i];
	}
	sort(a+1,a+1+n);
	int ans = 0;
	for(int i = 1; i <= n/2; ++i){
		ans = max(a[i*2]-a[i*2-1],ans);
	}
	cout << ans << '\n';
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> t;
	while(t--){
		solve();
	}
}

C. MEX rose

可以乱改求 \(mex\)

code
#include<bits/stdc++.h>
using namespace std;
const int NN = 2e5 + 8;
int t;
int n,k;
int a[NN];
void solve(){
	int cnt1 = 0,cnt2 = 0;
	cin >> n >> k;
	for(int i = 1; i <= n; ++i){
		cin >> a[i];
		if(a[i] == k) ++cnt1;
	}
	sort(a+1,a+1+n);
	for(int i = 1; i <= n; ++i){
		if(a[i] < k && a[i] != a[i-1]) ++cnt2;
	}
	cout << max(cnt1,k-cnt2) << '\n';
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> t;
	a[0] = -1;
	while(t--){
		solve();
	}
} 

D. A and B

求让一种字符连在一起的最小步数

我们首先提取出 \(a,b\) 两种字符的下标组,并且重新编辑位置为该字母前面与它不同的字符数量

然后我们最后的问题就变为了每一次操作 \(+1/-1\) 求最小的使得所有数相同的操作次数。

这个问题十分经典,就是让所有数变成中位数即可

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN = 2e5 + 8;
int t,n;
int a[NN],b[NN],lena,lenb;
string s;
void solve(){
	cin >> n; 
	cin >> s;
	lena = lenb = 0;
	for(int i = 0,dela = 0,delb = 0; i < n; ++i){
		if(s[i] == 'a'){
			a[++lena] = i-dela;
			++dela;
		}
		else{
			b[++lenb] = i-delb;
			++delb;
		}
	}
	int mda = (lena + 1) / 2,mdb = (lenb + 1) / 2;
	ll ansa = 0,ansb = 0;
	for(int i = 1; i <= lena; ++i){
		ansa += abs(a[i]-a[mda]);
	}
	for(int i = 1; i <= lenb; ++i){
		ansb += abs(b[i]-b[mdb]);
	}
	cout << min(ansa,ansb) << '\n';
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> t;
	while(t--){
		solve();
	}
} 

E. Hidden Knowledge of the Ancients

求长度为 \([l,r]\) 的有 \(k\) 种数的子区间的个数

第一种方法是两套双指针,然后对于每一个 \(l\),维护从 \(l\) 开始有 \(k\) 种数的最小区间和最大区间

本人写了第二种复杂很多的算法,我们可以发现,一个对于一个区间元素种类能够做出贡献,当且仅当在这个包含它的区间内并且在它前面没有和它相同的数(相当于一个矩形加)

然后我们需要找到有 \(k\) 种元素的区间个数,这个可以通过离线询问 + 扫描线解决

code
#include<bits/stdc++.h>
using namespace std;
const int NN = 2e5 + 8;
int t;
struct Seg{
	int l,r;
	int maxn,minn,tag;
	#define ls(x) (x << 1)
	#define rs(x) (x << 1 | 1)
	#define l(x) tree[x].l
	#define r(x) tree[x].r
	#define maxn(x) tree[x].maxn
	#define minn(x) tree[x].minn
	#define tag(x) tree[x].tag
}tree[NN << 2];
void addlz(int x,int num){
	maxn(x) += num; minn(x) += num;
	tag(x) += num;
}
void pushdown(int x){
	addlz(ls(x),tag(x)), addlz(rs(x),tag(x));
	tag(x) = 0;
}
void pushup(int x){
	maxn(x) = max(maxn(ls(x)),maxn(rs(x)));
	minn(x) = min(minn(ls(x)),minn(rs(x)));
}
void build(int x,int l,int r){
	l(x) = l;r(x) = r;minn(x) = maxn(x) = tag(x) = 0;
	if(l == r) return;
	int mid = (l + r) / 2;
	build(ls(x),l,mid);build(rs(x),mid+1,r);
	return;
}
void modify(int x,int l,int r,int num){
	if(l <= l(x) && r(x) <= r){
		addlz(x,num);
		return;
	}
	int mid = (l(x) + r(x)) / 2;
	pushdown(x);
	if(l <= mid) modify(ls(x),l,r,num);
	if(mid+1 <= r) modify(rs(x),l,r,num);
	pushup(x);
}
int findl(int x,int num){
	if(l(x) == r(x)) return maxn(x) == num ? l(x) : -1;
	pushdown(x);
	if(maxn(ls(x)) >= num) return findl(ls(x),num);
	else return findl(rs(x),num);
}
int findr(int x,int num){
	if(l(x) == r(x)) return minn(x) == num ? l(x) : -1;
	pushdown(x);
	if(minn(rs(x)) <= num) return findr(rs(x),num);
	else return findr(ls(x),num);
}

int n,k,l,r;
int a[NN];
int pre[NN],b[NN],lenb;
struct Oper{
	int pos,num;
};
vector<Oper> op[NN];
void solve(){
	long long ans = 0;
	cin >> n >> k >> l >> r;
	build(1,1,n);
	op[0].clear();
	for(int i = 1; i <= n; ++i){
		cin >> a[i];
		b[i] = a[i];
		pre[i] = 0;op[i].clear();
	}
	sort(b+1,b+1+n);
	lenb = unique(b+1,b+1+n) - b - 1;
	for(int i = 1; i <= n; ++i) a[i] = lower_bound(b+1,b+1+lenb,a[i]) - b;
	for(int i = 1; i <= n; ++i){
		op[pre[a[i]]].push_back({i,1});
		op[i].push_back({i,-1});
		pre[a[i]] = i;
	}
	for(int i = 0; i <= n; ++i){
		int fr = findr(1,k),fl = findl(1,k);
		if(i != 0 && fr != -1 && fl != -1){
			fr = min(i+r-1,fr);
			fl = max(i+l-1,fl);
			ans += max(0,fr - fl + 1);
		}
		for(int j = 0; j < op[i].size(); ++j){
			modify(1,op[i][j].pos,n,op[i][j].num);
		}
	}
	cout << ans << '\n';
}

int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> t;
	while(t--){
		solve();
	} 
}

F. Nezuko in the Clearing

tag:二分答案

我们首先可以贪心地发现,如果说我们知道最后的休息次数,那么我们只需要让每次的连续走的步数更加平均即可,这样一定就可以让花费的生命值最小

那么我们知道了验证方法,我们进行二分答案即可

code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int NN = 0;
int t,h,d;
bool check(int num){//二分休息次数 
	int stlen = d / (num+1), add = d % (num+1);
	return num + h - (num + 1) * (stlen * (stlen + 1) / 2) - add * (stlen + 1) >= 0;
}
void solve(){
	cin >> h >> d;
	int st = 0;
	if(h == 1) st = 1;
	else --h;
	
	int ans = 0,l = 0,r = d;
	while(l <= r){
		int mid = (l + r) / 2;
		if(check(mid)) ans = mid,r = mid - 1;
		else l = mid + 1;
	}
	cout << ans + st + d << '\n';
}
signed main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> t;
	while(t--){
		solve();
	}
}

G. Buratsuta 3

posted @ 2025-10-05 07:39  ricky_lin  阅读(27)  评论(0)    收藏  举报