ABC426题解

A. OS Versions

code
#include<bits/stdc++.h>
using namespace std;
string s;
int x,y;
int main(){
	cin >> s;
	if(s[0] == 'O') x = 0;
	else if(s[0] == 'S') x = 1;
	else x = 2;
	
	
	cin >> s;
	if(s[0] == 'O') y = 0;
	else if(s[0] == 'S') y = 1;
	else y = 2;
	
	if(x >= y) puts("Yes");
	else puts("No");
} 

B. The Odd One Out

code
#include<bits/stdc++.h>
using namespace std;
int main(){
	string s;
	cin >> s;
	sort(s.begin(),s.end()); 
	if(s[0] != s[1]) cout << s[0];
	else cout << s[s.size()-1];
}

C. Upgrade Required

我们只需要用优先队列维护即可,我们可以发现,这种操作会让序列中的数的种数减少,所以可以保证复杂度为 \(O(n\log n)\)

code
#include<bits/stdc++.h>
using namespace std;
int N,Q;
struct PC{
	int num;
	int ver;
	bool operator < (const PC &x)const{
		return ver > x.ver;
	}
};

priority_queue<PC> q;

int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> N >> Q;
	for(int i = 1; i <= N; ++i) q.push({1,i});
	
	while(Q--){
		int ans = 0;
		int x,y;
		cin >> x >> y;
		while(!q.empty() && q.top().ver <= x){
			ans += q.top().num;
			q.pop();
		}
		if(ans != 0) q.push({ans,y});
		cout << ans << '\n';
	}
	return 0;
}

D. Pop and Insert

就是找到最长的连续的 \(0\) 和最长的连续的 \(1\) 的个数,然后求出分别对应需要的操作数,最后求 \(min\) 即可

code
#include<bits/stdc++.h>
using namespace std;
int T;
int n,cnt[2],res[2];
string s;
void solve(){
	cnt[0] = cnt[1] = 0;
	res[0] = res[1] = 0;
	cin >> n >> s;
	int now = 0;
	for(int i = 0; i < n; ++i){
		++cnt[s[i]-'0'];
		if(i != 0 && s[i] != s[i-1]){
			int pre = s[i-1] - '0';
			res[pre] = max(res[pre],now);
			now = 0;
		}
		++now;
	}
	int pre = s[n-1] - '0';
	res[pre] = max(res[pre],now);
	now = 0;
	cout << min(n - res[0] * 2 + cnt[0], n - res[1] * 2 + cnt[1]) << '\n';
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> T;
	while(T--){
		solve();
	}
}

E. Closest Moment

tag:计算几何 三分

十分讨厌有些时候 \(double\) 下,两个应该相同的数相减,结果得到一个 \(-1e15\) 导致 $\sqrt \ $ 之后得到 \(nan\) 的情况(让我调了好久好久)

code for 计算几何
#include<bits/stdc++.h>
using namespace std;
int T;
int n,cnt[2],res[2];
string s;
void solve(){
	cnt[0] = cnt[1] = 0;
	res[0] = res[1] = 0;
	cin >> n >> s;
	int now = 0;
	for(int i = 0; i < n; ++i){
		++cnt[s[i]-'0'];
		if(i != 0 && s[i] != s[i-1]){
			int pre = s[i-1] - '0';
			res[pre] = max(res[pre],now);
			now = 0;
		}
		++now;
	}
	int pre = s[n-1] - '0';
	res[pre] = max(res[pre],now);
	now = 0;
	cout << min(n - res[0] * 2 + cnt[0], n - res[1] * 2 + cnt[1]) << '\n';
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> T;
	while(T--){
		solve();
	}
}

另一种方法是三分,我们通过三分的方法,可以对于每一段时间,求这一段时间内的最小距离(因为走的是直线,所以距离的变化最复杂也就是先变小再变大)

F. Clearance

tag: 线段树

我们发现,我们的非零数一定是逐渐减少的,然后我们的一个操作最后的答案就是 \(\text{(操作前非零数的个数)} \times k - 该次操作溢出的答案\)

至于维护,我们使用线段树即可。很容易发现,我们每一位都只会变成 \(0\) 进行一次,也就是说,在正常的线段树操作之外,我们只需要进行 \(n\) 次遍历到底的单点修改。

最后复杂度 \(O((n+q)\log n)\)

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll NN = 3e5 + 8,INF = 1e18;
int n,q;
ll a[NN];

struct Seg{
	int l,r;
	ll minn,zero;
	ll 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 minn(x) tree[x].minn
	#define tag(x) tree[x].tag
	#define zero(x) tree[x].zero
}tree[NN << 2]; 
ll tot;
ll update(int);
ll addlz(int x,ll num){
	tag(x) += num;
	if(l(x) == r(x) && minn(x) <= num){
		zero(x) = 1;
		ll ret = minn(x);
		minn(x) = INF;
		return num - ret;
	}
	minn(x) -= num;
	if(minn(x) <= 0){
		return update(x);
	}
	return 0;
}
ll pushdown(int x){
	ll ret = addlz(ls(x),tag(x)) + addlz(rs(x),tag(x));
	tag(x) = 0;
	return ret;
}
void pushup(int x){
	minn(x) = min(minn(ls(x)),minn(rs(x)));
	zero(x) = zero(ls(x)) + zero(rs(x));
}
ll update(int x){
	ll ret = pushdown(x);
	pushup(x);
	return ret;
}
void build(int x,int l,int r){
	l(x) = l;r(x) = r;
	if(l == r){
		minn(x) = a[l];return;
	}
	int mid = (l + r) / 2;
	build(ls(x),l,mid); build(rs(x),mid+1,r);
	pushup(x);
}
ll modify(int x,int l,int r,ll num){
	if(x == 1){
		tot = 0;
	}
	if(l <= l(x) && r(x) <= r){
		tot += addlz(x,num);
		return zero(x);
	}
	ll ret = 0;
	int mid = (l(x) + r(x)) / 2;
	pushdown(x);
	if(l <= mid) ret += modify(ls(x),l,r,num);
	if(mid + 1 <= r) ret += modify(rs(x),l,r,num);
	pushup(x);
	return ret;
}
ll get(int x,int l,int r){
	if(l <= l(x) && r(x) <= r) return zero(x);
	int mid = (l(x) + r(x)) / 2;
	ll ret = 0;
	if(l <= mid) ret += get(ls(x),l,r);
	if(mid + 1 <= r) ret += get(rs(x),l,r);
	return ret;
}
int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> n;
	for(int i = 1; i <= n; ++i) cin >> a[i];
	build(1,1,n);
	
	cin >> q;
	while(q--){
		ll l,r,k;
		cin >> l >> r >> k;
		ll pzr = get(1,l,r);
		ll zr = modify(1,l,r,k);
//		cout << pzr << " " << (r-l+1-pzr) << " " << (r-l+1-pzr) * k << " " << tot << '\n';
		cout << (r-l+1-pzr) * k - tot << '\n';
	}
} 

G. Range Knapsack Query

tag: 离线询问 分治 DP

如果询问全是从 \(1\) 开始,那么我们的 \(DP\) 部分就很简单。

但是询问需要对于一个区间求值,我们怎么快速求呢?可以分治处理询问,用 \(O(\log)\) 的时间代价,把两个自由端的问题,转化为一个自由端的问题。

我们首先对区间进行分治,每次维护从分治区间的中点向左/右的 \(DP\) 值。

然后对于该分治区间,我们只处理越过分治区间中点的询问的答案。

至于其他区间的答案,我们留给子区间处理。

时间复杂度:\(O(K(Q+N\log N))\)

code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN = 2e4 + 8,MM = 5e2 + 8,QQ = 2e5 + 8;
int n,Q;
ll W[NN],V[NN];
ll ftl[NN][MM],ftr[NN][MM];
ll ans[QQ];

struct Query{
	int L,R,C,id;
};

void solve(int l,int r,vector<Query> &q){
	if(l == r){
		for(int i = 0; i < q.size(); ++i){
			int id = q[i].id, lim = q[i].C;
			if(W[l] <= lim) ans[id] = V[l];
			else ans[id] = 0;
		}
		return;
	}
	vector<Query> lt,rt;
	int mid = (l + r) / 2;
	for(int i = 0; i <= 500; ++i)
		ftl[mid+1][i] = ftr[mid][i] = 0;
	
	for(int i = mid; i >= l; --i){
		for(int j = 0; j <= 500; ++j){
			if(j < W[i]) ftl[i][j] = 0;
			ftl[i][j] = max(ftl[i][j],ftl[i+1][j]);
			if(j <= 500-W[i]) ftl[i][j+W[i]] = ftl[i+1][j] + V[i];
		}
	}
	for(int i = mid+1; i <= r; ++i)
		for(int j = 0; j <= 500; ++j){
			if(j < W[i]) ftr[i][j] = 0;
			ftr[i][j] = max(ftr[i][j],ftr[i-1][j]);
			if(j <= 500-W[i]) ftr[i][j+W[i]] = ftr[i-1][j] + V[i];
		}
	for(int i = 0; i < q.size(); ++i){
		if(q[i].R <= mid) lt.push_back(q[i]);
		else if(q[i].L > mid) rt.push_back(q[i]);
		else{
			int id = q[i].id, lim = q[i].C, l = q[i].L, r = q[i].R;
			for(int j = 0; j <= lim; ++j){
				ans[id] = max(ans[id],ftl[l][j] + ftr[r][lim-j]);
			}
		}
	}
	solve(l,mid,lt);
	solve(mid+1,r,rt);
} 

int main(){
	ios::sync_with_stdio(false),cin.tie(0);
	cin >> n;
	for(int i = 1; i <= n; ++i){
		cin >> W[i] >> V[i];
	}
	cin >> Q;
	vector<Query> q;
	for(int i = 1,l,r,c; i <= Q; ++i){
		cin >> l >> r >> c;
		q.push_back({l,r,c,i});
	}
	solve(1,n,q);
	
	for(int i = 1; i <= Q; ++i) cout << ans[i] << '\n';
}
posted @ 2025-10-05 07:48  ricky_lin  阅读(167)  评论(0)    收藏  举报