文章目录
刷题之Codeforces Round #756 (Div. 3)
[1611A]. Make Even
- 思路:只有四种情况。若最后一位为偶,需要 0 0 0 次;否则若第一位为偶,只需整体反转,需要 1 1 1 次;否则若中间某位为偶,先反转到第一位,再转到最后一位,需要 2 2 2 次;否则无解,即所有位均为奇数。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
int cas, ans;
string s;
bool checkeven(int x){
if((s[x] - '0') % 2 == 0) return 1;
else return 0;
}
int main(){
cin >> cas;
while(cas--){
cin >> s;
if(checkeven(s.size() - 1)) ans = 0;
else if(checkeven(0)) ans = 1;
else{
int ok = 0;
rep(i, 0, (int)s.size() - 1){
if(checkeven(i)){
ok = 1;
break;
}
}
ans = (ok ? 2 : -1);
}
cout << ans << endl;
}
}
[1611B]. Team Composition: Programmers and Mathematicians
-
思路:不妨设 a ≤ b a\le b a≤b,否则交换一下。将所有 s u m = a + b sum=a+b sum=a+b 均分为四份(多出来的余数忽略,反正也一定组不成队),考虑 a a a 占据的数量。
-
若 a ≤ s u m 4 a\le \dfrac{sum}{4} a≤4sum, 则每个 a a a 对应(如图,同一列的) 3 3 3 个 b b b,最后多余的 b b b 只得无人配对。此时队伍数量为 a a a。
b b b b b b b b b b b b b b b a a a a b -
若 s u m 4 < a ≤ s u m 2 \dfrac{sum}{4}<a\le\dfrac{sum}{2} 4sum<a≤2sum,则每一列均满足,为 2 2 2 个 a a a, 2 2 2 个 b b b 或是 1 1 1 个 a a a, 3 3 3 个 b b b。此时队伍数量为 a + b 4 \dfrac{a+b}{4} 4a+b。
b b b b b b b b b b a a a a b a a a a a
-
于是答案在 a a a 和 a + b 4 \dfrac{a+b}{4} 4a+b 中取较小即可。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 1e5 + 5;
const db eps = 1e-10;
int cas, n, a, b, c, ans;
int main(){
cin >> cas;
while(cas--){
cin >> a >> b;
if(a > b) swap(a, b);
cout << min(a, (a + b) / 4) << endl;
}
}
[1611C]. Polycarp Recovers the Permutation
- 思路:
- 首先检查无解:由于 p p p 序列为 1 ∼ n 1\sim n 1∼n 的排列,所以 n n n 为最大值,一定是所剩最后一个数字,即只能排到 a a a 序列的两端。所以若 a a a 序列两端均不为 n n n,则一定无解。
- 其次还原
p
p
p 序列。只需要对
a
a
a 序列做逆过程,即每次选择两边较大数字,插入
p
p
p 序列对应左/右端即可。这里可以直接用双端队列
deque
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 2e5 + 5;
const db eps = 1e-10;
int cas, n, a[N];
int l, r;
deque<int> p;
int main(){
cin >> cas;
while(cas--){
cin >> n;
rep(i, 1, n) cin >> a[i];
if(a[1] != n && a[n] != n) puts("-1");
else{
l = (a[1] == n ? 2 : 1), r = (a[n] == n ? n - 1 : n);
p.clear();
p.push_back(n);
while(l < r){
if(a[l] > a[r]) p.push_front(a[l++]);
else p.push_back(a[r--]);
}
if(l == r) p.push_back(a[l]);
while(p.size()){
cout << p.front() << " ";
p.pop_front();
}
cout << endl;
}
}
}
[1611D]. Weights Assignment For Tree Edges
- 思路:要求
d
i
s
t
[
p
i
]
<
d
i
s
t
[
p
i
+
1
]
dist[p_i]<dist[p_{i+1}]
dist[pi]<dist[pi+1],不妨构造
d
i
s
t
[
p
i
]
=
i
dist[p_i]=i
dist[pi]=i。
- 首先判断是否有解:要求每个节点的 d i s t dist dist 大于其祖先的 d i s t dist dist。从根节点 d f s dfs dfs 搜一遍即可。
- 直接构造:对于每个节点,已经构造出其本身的 d i s t dist dist 和其父节点的 d i s t dist dist,则此边即为两值相减。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 2e5 + 5;
const db eps = 1e-10;
int cas, n, root, dist[N], pre[N], ans[N];
VI nxt[N];
bool dfs(int root, int rank){
for(auto i : nxt[root]){
if(dist[i] <= rank) return 0;
if(!dfs(i, dist[i])) return 0;
}
return 1;
}
int main(){
cin >> cas;
while(cas--){
cin >> n;
rep(i, 1, n) nxt[i].clear();
rep(i, 1, n){
cin >> pre[i];
if(pre[i] == i) root = i;
else nxt[pre[i]].push_back(i);
}
rep(i, 1, n){
int x; cin >> x;
dist[x] = i;
}
if(!dfs(root, dist[root])) puts("-1");
else{
rep(i, 1, n) cout << dist[i] - dist[pre[i]] << " ";
cout << endl;
}
}
}
[1611E1]. Escape The Maze (easy version)
-
题意:一棵树, V V V 开始在根节点,有一些其他人开始在其他不同节点 x i x_i xi。每次每个人可移动到相邻节点或不移动。问 V V V 能否到达一个叶子节点而中途不遇到其他人?
-
思路:对于一个节点 a a a,若 V V V 能先于其他人到达 a a a,等价于 a a a 到根节点的距离小于 a a a 到最近有其他人的节点的距离。题目即询问是否有节点 a a a 这样的叶子节点。
-
朴素想法:可以查询每个节点到根节点的距离和到所有 x i x_i xi 的最短距离,比较大小即可。但是这样的复杂度为 O ( n k ) O(nk) O(nk),需要优化。
-
优化:发现可以对所有点( x i x_i xi 和根节点)依次 b f s bfs bfs 查寻最短距离,只要遇到根节点先到达的叶子节点,此叶子便是可行解。
其实就是一个多源bfs,但是之前没想到
-
//多源bfs
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 2e5 + 5;
const db eps = 1e-10;
int cas, n, k;
int fr[N], dep[N], vis[N], minfr[N];
VI edge[N];
queue<PII> q; //PII第一个值存点,第二个值存来自的点的类型(朋友——2 or 根节点——1)
bool bfs(){
memset(vis, 0, sizeof(vis));
rep(i, 1, k){ //一定先入朋友
vis[fr[i]] = 1;
q.push({fr[i], 0});
}
vis[1] = 1; //再入根节点
q.push({1, 1});
while(q.size()){
PII tmp = q.front(); q.pop();
int now = tmp.first;
if(now != 1 && edge[now].size() == 1 && tmp.second == 1) return 1; //找到
for(auto i : edge[now]){
if(vis[i]) continue;
vis[i] = 1;
q.push({i, tmp.second});
}
}
return 0;
}
int main(){
// freopen("1.in","r",stdin);
cin >> cas;
while(cas--){
cin >> n >> k;
rep(i, 1, k) cin >> fr[i];
rep(i, 1, n) edge[i].clear();
rep(i, 1, n - 1){
int x, y; cin >> x >> y;
edge[x].push_back(y), edge[y].push_back(x);
}
while(q.size()) q.pop();
puts(bfs() ? "YES" : "NO");
}
}
[1611E2]. Escape The Maze (hard version)
- 题意:询问至少需要保留几个人才能保证一定阻止 V V V 到达叶子节点。
- 思路:其实是询问几个人是真正有用的。那怎么判定有用呢,当 V V V 走到一个点 a a a 发现有别人已经到达过 a a a 时,那这个人就是有用的。只要把所有有用的人存起来就好啦。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 2e5 + 5;
const db eps = 1e-10;
int cas, n, k;
int fr[N], dep[N], vis[N], minfr[N], from[N]; //from存来自哪个点
VI edge[N];
queue<int> q;
set<int> st;
int bfs(){
memset(vis, 0, sizeof(vis));
rep(i, 1, k){ //先入朋友
vis[fr[i]] = 1, from[fr[i]] = fr[i];
q.push(fr[i]);
}
vis[1] = 1, from[1] = 1; //再入根节点
q.push(1);
while(q.size()){
int now = q.front(); q.pop();
if(now != 1 && edge[now].size() == 1 && from[now] == 1) return -1;
for(auto i : edge[now]){
if(vis[i]){
if(from[now] == 1 && from[i] != 1) st.insert(from[i]); //从根节点来到一个朋友已到过的节点
continue;
}
vis[i] = 1, from[i] = from[now];
q.push(i);
}
}
return st.size();
}
int main(){
// freopen("1.in","r",stdin);
cin >> cas;
while(cas--){
cin >> n >> k;
rep(i, 1, k) cin >> fr[i];
rep(i, 1, n) edge[i].clear();
rep(i, 1, n - 1){
int x, y; cin >> x >> y;
edge[x].push_back(y), edge[y].push_back(x);
}
while(q.size()) q.pop();
st.clear();
printf("%d\n", bfs());
}
}
[1611F]. ATM and Students
对于维护连续一段满足要求,当然想到双指针。但是开始一直在想固定右端点移动左端点,发现不太能做。
- 思路:应该固定左端点,不断向右寻找右端点(因为需要保证每一个
(
l
,
r
)
(l,r)
(l,r) 都满足条件,所以需要右端点从小到大每次寻找检查更新)。
- 当左端点固定时,右端点更新到不能再增加,便停止,比较记录。
- 再更新左端点,如此遍历即可。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define db double
#define VI vector<int>
#define PII pair<int, int>
const db Pi = 3.141592653589793;
const int INF = 0x7fffffff;
const int N = 2e5 + 5;
const db eps = 1e-10;
int cas, n, ansl, ansr, maxn;
ll now, a[N], s;
int main(){
cin >> cas;
while(cas--){
cin >> n >> s;
rep(i, 1, n) cin >> a[i];
int t = 1;
while(s + a[t] < 0) t++;
ansl = ansr = 0, maxn = 0;
now = a[t];
for(int l = t, r = t; l <= n; l++){
while(r + 1 <= n && s + now + a[r + 1] >= 0) now += a[++r];
if(s + now >= 0 && maxn < r - l + 1){
ansr = r, ansl = l;
maxn = r - l + 1;
}
now -= a[l];
}
if(maxn <= 0) puts("-1");
else cout << ansl << " " << ansr << endl;
}
}