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
如果询问全是从 \(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';
}
本文来自博客园,作者:ricky_lin,转载请注明原文链接:https://www.cnblogs.com/rickylin/p/19126212/ABC426

浙公网安备 33010602011771号