刷题之Codeforces Round #756 (Div. 3)

刷题之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 ab,否则交换一下。将所有 s u m = a + b sum=a+b sum=a+b 均分为四份(多出来的余数忽略,反正也一定组不成队),考虑 a a a 占据的数量。

    1. a ≤ s u m 4 a\le \dfrac{sum}{4} a4sum, 则每个 a a a 对应(如图,同一列的) 3 3 3 b b b,最后多余的 b b b 只得无人配对。此时队伍数量为 a a a

      bbbbb
      bbbbb
      bbbbb
      aaaab
    2. s u m 4 < a ≤ s u m 2 \dfrac{sum}{4}<a\le\dfrac{sum}{2} 4sum<a2sum,则每一列均满足,为 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

      bbbbb
      bbbbb
      aaaab
      aaaaa

于是答案在 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

  • 思路
    1. 首先检查无解:由于 p p p 序列为 1 ∼ n 1\sim n 1n 的排列,所以 n n n 为最大值,一定是所剩最后一个数字,即只能排到 a a a 序列的两端。所以若 a a a 序列两端均不为 n n n,则一定无解。
    2. 其次还原 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
    1. 首先判断是否有解:要求每个节点的 d i s t dist dist 大于其祖先的 d i s t dist dist。从根节点 d f s dfs dfs 搜一遍即可。
    2. 直接构造:对于每个节点,已经构造出其本身的 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 这样的叶子节点。

    1. 朴素想法:可以查询每个节点到根节点的距离和到所有 x i x_i xi 的最短距离,比较大小即可。但是这样的复杂度为 O ( n k ) O(nk) O(nk),需要优化。

    2. 优化:发现可以对所有点( 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) 都满足条件,所以需要右端点从小到大每次寻找检查更新)。
    1. 当左端点固定时,右端点更新到不能再增加,便停止,比较记录。
    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 = 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;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值