Educational Codeforces Round 58 (Rated for Div. 2) 题解

A. Minimum Integer

题目链接:http://codeforces.com/contest/1101/problem/A

题意:找一个最小的正数x,能被d整除,x不能在[l, r]之间。

解题思路:直接看l是否大于等于d否则直接输出d,不然输出(r/d+1)*d。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 100;

int main() {
    int n; scanf("%d", &n);
    while(n--) {
        int l, r, d;
        scanf("%d%d%d",&l, &r, &d);
        if(l > d) {
            printf("%d\n", d);
        } else {
            printf("%d\n", d*(r/d + 1));
        }
    }
    return 0;
}



B. Accordion

http://codeforces.com/contest/1101/problem/B

题意:叫你找一个出一个最长的子序列,形如[:|||:],其中|可以有多个。

解题思路:从左往右找一个[记录位置pos1,然后从pos1开始找一个:记为pos2,从右往左找一个]记为pos4,从pos4开始找一个:记为pos3,然后在pos2和pos3之间找|,记录次数为cnt,然后输出cnt+4。

c++代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const int maxn = 5e5+100;
char s[maxn];

int main() {
    // freopen("1.in", "r", stdin);
    scanf("%s",s);
    int l = -1, lm = -1, rm = -1, r = -1;
    int ans = 0;
    int len = strlen(s);
    for(int i=0;i<len;i++) {
        if(s[i] == '[') {
            l = i;
            break;
        }
    }

    if(l == -1) {
        puts("-1");
        return 0;
    }

    for(int i=l+1;i<len;i++) {
        if(s[i] == ':') {
            lm = i;
            break;
        }
    }

    if(lm == -1) {
        puts("-1");
        return 0;
    }

    for(int i=len-1;i>lm;i--) {
        if(s[i] ==']') {
            r = i;
            break;
        }
    }

    if(r == -1) {
        puts("-1");
        return 0;
    }

    for(int i=r-1;i>lm;i--) {
        if(s[i] == ':') {
            rm = i;
            break;
        }
    }

    if(rm == -1) {
        puts("-1");
        return 0;
    }

    for(int i=lm+1;i<rm;i++) {
        if(s[i] == '|') {
            ans++;
        }
    }
    ans += 4;
    cout<<ans<<endl;

    return 0;
}



C. Division and Union

http://codeforces.com/contest/1101/problem/C

题意:给你n段区间,你要把所有的区间分为两个集合,不能找到一点能够在两个集合中的区间中都找到。

解题思路:很简单啊,直接将区间按照左端点排序,左端点相同的按照右端点排序,然后记录每个集合最大的r,当一个新区间的l小于其中一个集合的r时就换另一个,都不能就输出-1。能放入就放入然后更新集合的最大r就行。

#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <vector>
#include <set>
#include <cstring>
#include <map>
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const int maxn = 1e5+100;

struct node {
    int l, r, pos;
}p[maxn];
int ans[maxn];

bool cmp(node a, node b) {
    if(a.l == b.l) {
        return a.r < b.r;
    }
    return a.l < b.l;
}

int main() {
    //freopen("1.in", "r", stdin);
    int t;
    scanf("%d",&t);
    while(t--) {
        int n;
        scanf("%d",&n);
        bool flag = false;

        for(int i=1;i<=n;i++) {
            scanf("%d%d", &p[i].l, &p[i].r);
            p[i].pos = i;
        }

        int end[3];
        sort(p+1, p+1+n, cmp);
        ans[p[1].pos] = 1;
        end[1] = p[1].r;
        end[2] = -1;
        int cnt = 1;
        for(int i=2;i<=n;i++) {
            if(p[i].l <= end[1] && p[i].l <=  end[2]) {
                flag = true;
                break;
            }
            if(p[i].l <= end[1]) {
                ans[p[i].pos] = 1;
                if(p[i].r > end[1])
                    end[1] = p[i].r;
                cnt++;
            } else {
                ans[p[i].pos] = 2;
                if(p[i].r > end[2])
                    end[2] = p[i].r;
            }
        }
        if(flag || cnt == n) {
            puts("-1");
            continue;
        } else {
            for(int i=1;i<=n;i++) {
                printf("%d", ans[i]);
                if(i != n) printf(" ");
                else printf("\n");
            }
        }
    }

    return 0;
}



D. GCD Counting

http://codeforces.com/contest/1101/problem/D

题意:给你一棵树,树的每个节点上有个权值,要求你找一条路径,路径上节点权值的gcd不为1,并且路径要最长。

解题思路:直接树上点分治,分治下来的每一个重心跑一个dfs,找到最长的路径和次长的路径,然后加起来维护最大值就行了。复杂度(nlog(n))

#include <bits/stdc++.h>

using namespace std;
const int maxn = 2e5 + 100;

int num[maxn], n, centroid[maxn], ans, subtree_size[maxn];

vector<int> ve[maxn], prim_num[maxn];

bool vis[maxn];

void get_prim_num() {
    for (int i = 2; i < maxn; i++) {
        if (vis[i]) continue;
        prim_num[i].push_back(i);
        for (int j = i + i; j < maxn; j += i) {
            prim_num[j].push_back(i);
            vis[j] = true;
        }
    }
}

void init() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &num[i]);

    for (int i = 1; i < n; i++) {
        int a, b;
        scanf("%d%d", &a, &b);
        a--, b--;
        ve[a].push_back(b);
        ve[b].push_back(a);
    }
}

//计算子树的大小
int compute_subtree_size(int v, int p) {
    int c = 1;
    for (int i = 0; i < ve[v].size(); i++) {
        int w = ve[v][i];
        if (w == p || centroid[w]) continue;
        c += compute_subtree_size(w, v);
    }
    return subtree_size[v] = c;
}

//找中心为根节点的树的深度
pair<int, int> search_centroid(int v, int p, int t) {
    pair<int, int> res = make_pair(INT_MAX, -1);
    int m = 0, s = 1;
    for (int i = 0; i < ve[v].size(); i++) {
        int w = ve[v][i];
        if (w == p || centroid[w]) continue;

        res = min(res, search_centroid(w, v, t));

        m = max(m, subtree_size[w]);
        s += subtree_size[w];
    }

    m = max(m, t - s);
    res = min(res, make_pair(m, v));

    return res;
}

//找以重心为根节点的树的深度
int dfs(int v, int p, int prim, int len) {
    int Max_len = len;
    for (int i = 0; i < ve[v].size(); i++) {
        int w = ve[v][i];
        if (centroid[w] || w == p) continue;
        if (num[w] % prim == 0) {
            Max_len = max(dfs(w, v, prim, len + 1), Max_len);
        }
    }

    return Max_len;
}

void checke(int v) {
    int va = num[v];
    if(va > 1) ans = max(ans, 1);
    for (int i = 0; i < prim_num[va].size(); i++) {
        int w = prim_num[va][i];
        int deep = 0;
        int Max = 0, cMax = 0;

        for (int j = 0; j < ve[v].size(); j++) {
            if(centroid[ve[v][j]]) continue;
            if(num[ve[v][j]] % w != 0) continue;

            deep = dfs(ve[v][j], v, w, 2);
            //找出最长路和次长路
            if(deep > Max) {
                cMax = Max;
                Max = deep;
            } else if(deep > cMax) {
                cMax = deep;
            }
        }
        if(cMax != 0) {
            cMax--;
        }
        ans = max(ans, Max + cMax);
    }
}

void solve_subproblem(int v) {
    compute_subtree_size(v, -1);
    int s = search_centroid(v, -1, subtree_size[v]).second;
    centroid[s] = true;

    for (int i = 0; i < ve[s].size(); i++) {
        int w = ve[s][i];
        if (centroid[w]) continue;
        solve_subproblem(w);
    }

    checke(s);
    centroid[s] = false;
}

int main() {
    freopen("1.in", "r", stdin);
    get_prim_num();
    init();
    solve_subproblem(0);
    printf("%d", ans);
    return 0;
}



E. Polycarp’s New Job

http://codeforces.com/contest/1101/problem/E

题意:q次询问,每次给你一个矩形的钞票告诉你你长和高,或者给你一个钱包的长和宽询问你是否能将所有的钞票放下,不考虑斜放的情况。

解题思路:首先定义矩形长大于宽,然后记录长的最大值的宽和最大值的长,询问钱包的时候直接比较长宽就行了。

#include <bits/stdc++.h>
using namespace std;

int main() {
    //freopen("1.in", "r", stdin);
    int n;
    scanf("%d",&n);
    int Max_x = -1, Max_y = -1;
    while(n--) {
        char s[5];
        int x, y;
        scanf("%s%d%d",s, &x, &y);
        if (x > y) swap(x, y);
        if(s[0] == '+') {
            if (x > Max_x) Max_x = x;
            if (y > Max_y) Max_y = y;
        } else {
            if(x >= Max_x && y >= Max_y) puts("YES");
            else puts("NO");
        }
    }
    return 0;
}



比赛感想:
  1. A题和B题反应都还不错很快无伤过了,然后做C题的时候想到了排序,然后脑子卡住了,不知道在瞎写什么,冷静了一会儿想到了维护两个集合最大的r。
  2. 看见大家都把E题给做出来了,就去看E题,发现E题真的是很简单,害怕出题人坑人,但是看大家都过得这么高兴也就想了一个简单的思路硬着头皮写完了。
  3. 最后还剩半个小时左右去搞D题,思路很顺畅就想到了树dp,搞了十多分钟发现自己的dp是真的菜啊,写不出来,然后没时间了破罐子破摔写了个暴力的dfs交了,很快就MLE了。没办法了进入贤者时间坐等比赛结束。
  4. 比赛结束之后准备碎觉,突然想到E题的钱包好像可以斜着放钞票,内心很沮丧感觉自己的E题jj咯。睡不着起来hack别人,发现总是unsuccessful有点郁闷就睡觉了,第二天起来发现E题就是这么智障,然后队友告诉我D题可以点分治,又告诉我D题可以树dp,菜得不想说话。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值