2023-03-19 22:48阅读: 539评论: 1推荐: 3

AtCoder Beginner Contest 294

A - Filter (abc294 a)

题目大意

给定一个数组,不改变原顺序,输出是偶数的数。

解题思路

模拟即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
while(n--){
int x;
cin >> x;
if (!(x & 1))
cout << x << ' ';
}
cout << '\n';
return 0;
}


B - ASCII Art (abc294 b)

题目大意

给定一个二维数组,将其中的0变成.,其余的126则变成AZ,输出变换后的数组。

解题思路

模拟即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int h, w;
cin >> h >> w;
while(h--){
string s;
for(int i = 0; i < w; ++ i){
int x;
cin >> x;
if (x == 0)
s += '.';
else
s += 'A' + (x - 1);
}
cout << s << '\n';
}
return 0;
}


C - Merge Sequences (abc294 c)

题目大意

给定两个严格升序数组A,B,且两两互不相同。令 C为 该两个数组合并后并重新升序排序。

输出原在数组A,B 的每个元素在数组C的位置。

解题思路

即对每个元素求,有多少个数比它小。

对于A中的每个元素 i,首先在 A中有 i1个元素比它小。其次对于在 B数组中,求 出前 x个都比它小。

i递增时,因为大小关系具有连续性,因此在 B数组中比它小的数可以在上次状态的基础上继续求。

即双指针法,时间复杂度为 O(n+m)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> a(n), b(m);
for(auto &i : a)
cin >> i;
for(auto &i : b)
cin >> i;
int cur = 0;
for(int i = 0; i < n; ++ i){
while(cur < m && a[i] > b[cur])
++ cur;
cout << (i + 1) + cur << ' ';
}
cout << '\n';
cur = 0;
for(int i = 0; i < m; ++ i){
while(cur < n && b[i] > a[cur])
++ cur;
cout << (i + 1) + cur << ' ';
}
cout << '\n';
return 0;
}


D - Bank (abc294 d)

题目大意

n个人编号 1n,依次发生 q次事件,分三种:

  • 1 呼叫编号最小的未叫到的号
  • 2 x 标号为 x的人来报道
  • 3 呼叫已呼叫但未报道的标号最小的人

对于每个第三事件,回答叫到标号是多少。

解题思路

第一事件一定是递增的,所以用一个变量维护就好了。

而第三事件,我们只需记录下每个人的报道状态,其叫到的号也是递增的,所以也用一个变量维护就好了。

因为保证数据合法,其实就没别的要考虑的了。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, q;
cin >> n >> q;
vector<int> vis(n);
int cur = 0;
int noww = 0;
while(q--){
int op;
cin >> op;
if (op == 1){
++ cur;
}else if (op == 2){
int id;
cin >> id;
-- id;
vis[id] = 1;
}else{
while(vis[noww] == 1)
++ noww;
cout << noww + 1 << '\n';
}
}
return 0;
}


E - 2xN Grid (abc294 e)

题目大意

给定两个长度均为L的数组a,b,求下标 i满足ai=bi 的数量。

每个数组以若干个二元组(v,l)依次表示,即有连续 l个值为 v,不断延续。

解题思路

因为长度有1012不能暴力匹配,但考虑到两个数组的切割点(就是二元组数量)只有 O(105),因此我们就以切割点,每块每块考虑就可以了。

感觉非常暴力啊

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
LL l;
int n, m;
cin >> l >> n >> m;
vector<array<LL, 2>> a(n), b(m);
for(auto &i : a)
cin >> i[0] >> i[1];
for(auto &i : b)
cin >> i[0] >> i[1];
LL ans = 0;
int aa = 0, bb = 0;
while(true){
LL minn = min(a[aa][1], b[bb][1]);
if (a[aa][0] == b[bb][0])
ans += minn;
a[aa][1] -= minn;
b[bb][1] -= minn;
if (a[aa][1] == 0)
++ aa;
if (b[bb][1] == 0)
++ bb;
if (aa == n || bb == m)
break;
}
cout << ans << '\n';
return 0;
}


F - Sugar Water 2 (abc294 f)

题目大意

两排糖水,糖成份x和水成分 y

从两排中各取一个糖水混合,得糖浓度 xi+xjxi+xj+yi+yj%

问所有方案中,糖浓度第 k大是多少。

解题思路

考虑一经典问题:两个数组a,b选各选一个数,求其和是第k大的数。

其解法是二分,当我们二分这个第 k大的和是 x后,我们就看是否有 k1ai+bj>x

暴力验证的复杂度是O(n2),但可以优化,即对a,b排序,然后遍历每个 ai,统计下b数组中大于xai的数量。因为ai递增,所以 xai递减,其b数组中的数量会越来越多,且每次求解继承前一状态继续求即可。因此可以双指针求解。(与C题做法一致)

bi>xai

因为有单调性的缘故,验证的复杂度可以降为O(n),总的复杂度是 O(nlogn)

而对于此题,很明显就是将ai+bi替换成了上述分式。

同样考虑二分值mid,但此时 没有上述的单调性:一个份糖水与另一份糖水混合在一起后其浓度我们找不到某种单调性优化验证的复杂度,做不到 O(n)(暴力验证是O(n2)

但我们还是考虑是否存在某些单调性,我们考虑其比较式子:

xi+xjxi+xj+yi+yj>mid

分母移项,得

xi+xj>mid×(xi+xj+yi+yj)

然后我们将i,j分离,得

(1mid)×ximid×yi>(mid1)×xj+mid×yj

这个式子其实就跟上述经典问题转换后的原式子是一样的形式,从这个式子我们就可以窥见另一种单调性:

对于满足原式的i,j的数量,相当于新数组 tmpa[i]=(1mid)×ximid×yi,tmpb[j]=(mid1)×xj+mid×yj,满足tmpa[i]>tmpb[j]的数量。

而这很显然对tmpa,tmpb数组排个序后,枚举每个 tmpa,求出小于其的 tmpb的元素数量,累计就可以了。因为大小具有传递性,因此其 tmpb的元素数量是递增的,可以从上一个状态继续求解,即双指针法。

因此验证的复杂度就是 O(nlogn+mlogm)

因此总的时间复杂度就是O((nlogn+mlogm)log109)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const double eps = 1e-12;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, m;
LL k;
cin >> n >> m >> k;
vector<array<LL, 2>> a(n), b(m);
for(auto &i : a)
cin >> i[0] >> i[1];
for(auto &i : b)
cin >> i[0] >> i[1];
double l = 0, r = 1;
auto check = [&](double x){
vector<double> tmpa(n), tmpb(m);
for(int i = 0; i < n; ++ i){
tmpa[i] = (1 - x) * a[i][0] - x * a[i][1];
}
for(int i = 0; i < m; ++ i){
tmpb[i] = (x - 1) * b[i][0] + x * b[i][1];
}
sort(tmpa.begin(), tmpa.end());
sort(tmpb.begin(), tmpb.end());
LL cnt = 0;
LL cur = 0;
for(int i = 0; i < n; ++ i){
while(cur < m && tmpb[cur] < tmpa[i])
++ cur;
cnt += cur;
}
return cnt <= k;
};
-- k;
while(r - l > eps){
double mid = (l + r) / 2;
if (check(mid))
r = mid;
else
l = mid;
}
cout << fixed << setprecision(11) << r * 100 << '\n';
return 0;
}


G - Distance Queries on a Tree (abc294 g)

题目大意

给定一棵树,边有边权,维护一下两种操作:

  • 1 x w 将第x条边边权改为 w
  • 2 u v 问从uv的最短路径长度

解题思路

首先随便选个点作为根。

如果没有修改操作,我们可以维护每个点到根节点的距离dis,记u,vlca(最近公共祖先)x,那u>v的距离就是 dis[u]+dis[v]2dis[x]

而如果有修改操作,每次修改则会影响到一棵子树的 dis值。从一棵树的 dfs序来看的话就是修改一个区间。

用线段树维护dfs序上的dis数组即可。

区间修改和单点查询。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
class segtree {
#define lson (root << 1)
#define rson (root << 1 | 1)
int n;
vector<LL> max;
vector<LL> lazy;
void build(int root, int l, int r, const vector<int> &v, const vector<LL> &val) {
if (l == r) {
lazy[root] = 0;
max[root] = val[v[l - 1]];
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid, v, val);
build(rson, mid + 1, r, v, val);
max[root] = std::max(max[lson], max[rson]);
lazy[root] = 0;
}
inline void pushdown(int root){
if (lazy[root]){
lazy[lson] += lazy[root];
max[lson] += lazy[root];
lazy[rson] += lazy[root];
max[rson] += lazy[root];
lazy[root] = 0;
}
}
void update(int root, int l, int r, int L, int R, LL val){
if (L <= l && r <= R){
lazy[root] += val;
max[root] += val;
return;
}
pushdown(root);
int mid = (l + r) >> 1;
if (L <= mid)
update(lson, l, mid, L, R, val);
if (R > mid)
update(rson, mid + 1, r, L, R, val);
max[root] = std::max(max[lson], max[rson]);
}
LL query(int root, int l, int r, int pos){
if (l == r)
return max[root];
pushdown(root);
int mid = (l + r) >> 1;
if (pos <= mid)
return query(lson, l, mid, pos);
else
return query(rson, mid + 1, r, pos);
}
public:
void build(const vector<int> &v, const vector<LL> &val){
n = v.size();
lazy.resize(4 * (n + 1), 0);
max.resize(4 * (n + 1), 0);
build(1, 1, n, v, val);
}
void update(int L, int R, LL val){
assert(1 <= L && R <= n && L <= R);
update(1, 1, n, L, R, val);
}
LL query(int pos){
return query(1, 1, n, pos);
}
};
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
vector<vector<array<LL, 2>>> edge(n);
vector<array<int, 3>> raw(n);
vector<int> deep(n);
vector<LL> dis(n);
vector<array<int, 31>> up(n);
for(int i = 1; i < n; ++ i){
int u, v, w;
cin >> u >> v >> w;
-- u, -- v;
edge[u].push_back({v, w});
edge[v].push_back({u, w});
raw[i] = {u, v, w};
}
vector<int> order;
vector<int> first(n);
vector<int> last(n);
function<void(int, int)> dfs = [&](int u, int fa){
first[u] = order.size();
order.push_back(u);
for(auto &[v, w] : edge[u]){
if (fa == v)
continue;
dis[v] = dis[u] + w;
deep[v] = deep[u] + 1;
up[v][0] = u;
dfs(v, u);
}
last[u] = order.size() - 1;
};
auto get_lca = [&](int u, int v){
if (deep[u] < deep[v])
swap(u, v);
int t = deep[u] - deep[v];
for(int i = 0; i < 31; ++ i){
if (t & (1 << i))
u = up[u][i];
}
for(int i = 30; i >= 0; -- i){
int uu = up[u][i], vv = up[v][i];
if (uu != vv){
u = uu;
v = vv;
}
}
return u == v ? u : up[u][0];
};
deep[0] = 0;
up[0][0] = 0;
dfs(0, 0);
segtree seg;
seg.build(order, dis);
for(int i = 1; i <= 30; ++ i){
for(int j = 0; j < n; ++ j){
up[j][i] = up[up[j][i - 1]][i - 1];
}
}
int q;
cin >> q;
while(q--){
int op;
cin >> op;
if (op == 1){
int id, val;
cin >> id >> val;
if (raw[id][2] == val)
continue;
auto [u, v, w] = raw[id];
if (deep[u] < deep[v])
swap(u, v);
LL cval = val - w;
seg.update(first[u] + 1, last[u] + 1, cval);
raw[id][2] = val;
}else{
int u, v;
cin >> u >> v;
-- u;
-- v;
LL dis1 = seg.query(first[u] + 1);
LL dis2 = seg.query(first[v] + 1);
int lca = get_lca(u, v);
LL dis3 = seg.query(first[lca] + 1);
cout << dis1 + dis2 - 2 * dis3 << '\n';
}
}
return 0;
}


Ex - K-Coloring (abc294 h)

题目大意

给定一张图,要求给点分配值域为[1,k]的点权,问有多少种方案数,使得每条边的两个顶点的点权都不同。

解题思路

<++>

神奇的代码


本文作者:~Lanly~

本文链接:https://www.cnblogs.com/Lanly/p/17234703.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(539)  评论(1编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.