首页 > 其他分享 >AtCoder Beginner Contest 309

AtCoder Beginner Contest 309

时间:2023-07-08 22:44:25浏览次数:56  
标签:AtCoder cout Beginner 309 int auto cin mid using

感觉F写了个乱搞做法

A - Nine (abc309 A)

题目大意

给定一个\(3 \times 3\)的网格,以及两个数字。

tu

问这两个数字是否水平相邻

解题思路

求出两个数字的横纵坐标,看是否横坐标相同,纵坐标差一即可。

读题不仔细,开题就WA了。

神奇的代码
#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 a, b;
    cin >> a >> b;
    -- a, -- b;
    if (abs(a % 3 - b % 3) == 1 && abs(a / 3 - b / 3) == 0)
        cout << "Yes" << '\n';
    else 
        cout << "No" << '\n';

    return 0;
}



B - Rotate (abc309 B)

题目大意

给定一个矩形。将最外围的数字顺时针旋转一格。

解题思路

可以模拟一个指针跑,也可以分别考虑四个边界的转移公式。

神奇的代码
#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;
    vector<string> a(n);
    for(auto &i : a)
        cin >> i;
    auto ans = a;
    for(int i = 0; i < n; ++ i){
        ans[0][i] = i ? a[0][i - 1]: a[1][0];
        ans[i][0] = i == n - 1 ? a[n - 1][1] : a[i + 1][0];
        ans[n - 1][i] = i == n - 1 ? a[n - 2][n - 1] : a[n - 1][i + 1];
        ans[i][n - 1] = i ? a[i - 1][n - 1] : a[0][n - 2];
    }
    for(auto &i : ans){
            cout << i;
        cout << '\n';
    }

    return 0;
}



C - Medicine (abc309 C)

题目大意

给定\(n\)种药,第 \(i\)种药吃 \(a_i\)天,每天 \(b_i\)粒。

问最早到第几天,吃的药的粒数 \(\leq K\)。

解题思路

我们可以从小到大枚举天数,直到某天吃的药的粒数\(\leq K\),但天数高达\(O(10^9)\),直接枚举会超时。

但是我们会发现,每天的吃的药的粒数会越来越少,因此每天吃的药的粒数和天数之间有单调性

于是我们可以二分天数,判断这一天吃的药的粒数是否\(\leq K\),进而排除一半的答案,搜索另一片。

神奇的代码
#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, k;
    cin >> n >> k;
    vector<array<int, 2>> a(n);
    for(auto &i : a)
        cin >> i[0] >> i[1];
    int l = 0, r = 1e9 + 7;
    auto check = [&](int x){
        LL tot = 0;
        for(auto &i : a){
            tot += i[1] * (i[0] >= x);
        }
        return tot > k;
    };
    while(l + 1 < r){
        int mid = (l + r) >> 1;
        if (check(mid))
            l = mid;
        else 
            r = mid;
    }
    cout << r << '\n';

    return 0;
}



D - Add One Edge (abc309 D)

题目大意

给定两个连通块,现在从这两个连通块中各选一个点,连一条边,问\(1\)号点和 \(n_1+n_2\)号点的最小距离的最大值。

解题思路

很显然,从\(1\)号点到 \(n_1+n_2\)号点,其路径是 \(1 \to l \to r \to n_1 + n_2\)。其中\(l\)号点在第一个连通块,\(r\)号点在第二个联通块,是我们选择连边的两个点,其中 \(l \to r\)距离是 \(1\),最大化总距离,那就是最大化 \(1 \to l\)和 \(r \to n_1 + n_2\)。

那就分别从\(1\)号点和 \(n_1 + n_2\)号点分别 \(BFS\),找到最远的距离的点,连边即是答案。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

const int inf = 1e9 + 7;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int n1, n2, m;
    cin >> n1 >> n2 >> m;
    vector<array<int, 2>> edges(m);
    vector<vector<int>> e(n1 + n2);
    for(int i = 0; i < m; ++ i){
        int u, v;
        cin >> u >> v;
        -- u, -- v;
        edges[i] = {u, v};
        e[u].push_back(i);
        e[v].push_back(i);
    }
    vector<int> dis(n1 + n2, inf);
    auto BFS = [&](int st){
        dis[st] = 0;
        queue<int> team;
        team.push(st);
        while(!team.empty()){
            int u = team.front();
            team.pop();
            for(auto &i : e[u]){
                int v = u ^ edges[i][0] ^ edges[i][1];
                if (dis[v] > dis[u] + 1){
                    dis[v] = dis[u] + 1;
                    team.push(v);
                }
            }
        }
    };
    BFS(0);
    BFS(n1 + n2 - 1);
    cout << *max_element(dis.begin(), dis.begin() + n1) + *max_element(dis.begin() + n1, dis.end()) + 1 << '\n';

    return 0;
}



E - Family and Insurance (abc309 E)

题目大意

给定一棵(?或者若干棵)树,以及\(m\)次操作。

每次操作将以\(x_i\)号点的子树,到根距离不超过\(y_i\) 的点都加一。

问有多少个点至少加了一。

解题思路

如果将问题简化成一条链,那很显然就是个区间加问题,差分一下,每次操作就变成一个点加一,一个点减一,最后再求一遍前缀和还原数组就解决了。

但本题是树,与链的区别是,每次操作是一个点加一,很多个点减一(并且减一的点不能很快找到)。

但考虑对这棵树\(DFS\),在某一刻,我们维护的信息其实就是当前点到根的这条链,于是问题貌似就解决了?

在\(DFS\)时,实时维护上述的差分数组该差分数组的和(即在搜索到该点时才考虑对该点操作的影响,影响都是对后续的影响),回溯的时候消除对该点操作影响到的差分数组即可。

神奇的代码
#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<vector<int>> edge(n);
    for(int i = 1; i < n; ++ i){
        int v;
        cin >> v;
        -- v;
        edge[v].push_back(i);
    }
    int ans = 0;
    vector<int> visit(n, false);
    vector<int> diff(n + 1, 0);
    int sum = 0;
    vector<int> ins(n, 0);
    for(int i = 0; i < m; ++ i){
        int x, y;
        cin >> x >> y;
        -- x;
        ins[x] = max(ins[x], y);
    }
    function<void(int, int)> dfs = [&](int u, int deep){
        visit[u] = 1;
        if (ins[u]){
            ++ sum;
            diff[min(n, deep + ins[u] + 1)] -= 1;
        }
        sum += diff[deep];
        if (sum)
            ++ ans;
        for(auto &v : edge[u]){
            dfs(v, deep + 1);
        }
        sum -= diff[deep];
        if (ins[u]){
            -- sum;
            diff[min(n, deep + ins[u] + 1)] += 1;
        }
    };
    for(int i = 0; i < n; ++ i){
        if (!visit[i])
            dfs(i, 0);
    }
    cout << ans << '\n';

    return 0;
}



F - Box in Box (abc309 F)

题目大意

给定\(n\)个三元组,可以交换三元组的元素的顺序。

问是否存在两个三元组,其一个三元组的三个元素都严格大于另一个三元组的三个元素。

解题思路

考虑二元组的做法。

如果不能交换,这就是一个典型的二维偏序问题。

如果能交换,枚举一下交换情况,可以发现在比较的时候,希望让最大值之间比较,最小值之间比较,而不是让一个二元组的最大值和一个二元组的最小值比较。

因此我们将二元组的元素排个序,小的在左,大的在右,然后再按第一位排序,此时可以发现用双指针法就解决了,当然也可以用二维偏序的解法比如树状数组解决。

从左到右枚举枚举一个二元组\(a_i\),然后找到第一个 \(a_j[0] > a_i[0]\),此时如果 \(\max_{k \geq j} a_k[1] > a_i[1]\),那就存在两个二元组满足上述条件了。而\(\max_{k \geq j} a_k[1]\)就是一个后缀最大值,事先预处理一下就可以 \(O(1)\)查询。

二维的解决了,考虑三维。

二维偏序转到三维偏序的一个做法是\(CDQ\)分治(然后花了几分钟学了下\(CDQ\)分治)。注意二维偏序的做法是保证一维有序的情况下处理另一维,而三维的话就是要保证两维都有序的情况下处理第三维。

如何保证两维都有序呢?首先我们对第一维排个序,这样就保证了一维有序,还有一维如何保证?如果我们对整个区间的第二维排序,那么第一维有序就不一定了。

能否不对整个区间排序?而取部分的区间?

首先对于一个区间\([l, r]\)是否存在答案,根据分治,答案的分布有三种可能,要么两个三元组都在 \([l, mid]\),要么都在 \([mid + 1, r]\),要么一个在左区间,一个在右区间,

前两种可能我们可以递归解决,对于第三种可能,我们就要找到两个区间的三元组的偏序关系,由于第一维事先排好序了,因此在总会有 \(a_i \leq a_j, i \in [l, mid], j \in [mid + 1, r]\) ,因此第一维可以忽略,此时就剩下第二维和第三维,问题就变成上述的二维问题,那么我们可以分别对\([l, mid],[mid + 1, r]\)的第二维排序,然后用上述提到的双指针法和后缀最大值解决。

但还有个问题,注意我们的第一维的关系是\(\leq\),而不是题目要求的严格小于,因此此时的第一维实际上不能忽略。万一第一维相等就不能成为答案,但由于我们对第二维排了序,因此第一维的大小是乱序的,似乎就没有什么规律。

细想一下,在之前的\(CDQ\)分治统计不严格偏序数量时,答案的正确性在于第一维关系是\(\leq\),符合题目要求,就可以忽略这一维,而此处的 \(\leq\)并不符合题目要求,不能忽略第一维,如果强行忽略,答案可能会不正确(考虑答案的范围就扩大成\(a_i[0] = a_j[0], a_i[1] < a_j[1], a_i[2] < a_j[2]\)),而如果能保证 \(<\),那么答案的正确性就能保证。为了保证有\(<\),考虑能否可以将\(mid\)设为 \(a_{mid + 1}[0] > a_{mid}[0]\)的地方?

但注意到, \(CDQ\)分治的时间复杂度是 \(O(n\log^2 n)\),其每次分治时的\(mid = \frac{l + r}{2}\) 是一个保证复杂度的关键之处,这样才保证每次分治处理的复杂度是\(f(n) + f(\frac{n}{2}) + f(\frac{n}{2}) + ...\),而每次分治因为涉及到排序,因此 \(f(n) = n\log n\) 。

为了找一个\(mid\)使得 \(a_{mid + 1}[0] > a_{mid}[0]\),同时又要保证复杂度,可以大胆猜想选择\(a_{mid}[0]\)的数的最左边或最右边(因为对第一维排了序,相同数都在一起,因此分界点就从中间位置左右偏移一下,找到第一个\(\neq a_{mid}[0]\)之处作为分治的分界点。虽然每次分治的区间大小不一定减半,但从值域上感觉会减半,并大胆猜想出题人不会卡,于是按此写了写发现过了。

如何找到从中间位置左右偏移的点,对第一维排序后预处理一下每个第一维值的最小下标和最大下标即可。

神奇的代码
#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;
    vector<array<int, 3>> a(n);
    for(auto &i : a){
        for(auto &j : i)
            cin >> j;
        sort(i.begin(), i.end());
    }
    sort(a.begin(), a.end(), [&](const auto& x, const auto& y){
            return x[0] < y[0];
        });
    map<int, int> posl, posr;
    for(int i = n - 1; i >= 0; -- i){
        posl[a[i][0]] = i;
    }
    for(int i = 0; i < n; ++ i){
        posr[a[i][0]] = i;
    }
    bool ok = 0;
    function<void(int, int)> solve = [&](int l, int r){
        if (l >= r - 1)
            return;
        int mid = posl[a[(l + r) >> 1][0]];
        if (mid <= l)
            mid = posr[a[(l + r) >> 1][0]] + 1;
        if (mid >= r)
            return;
        solve(l, mid);
        solve(mid, r);

        sort(a.begin() + l, a.begin() + mid, [&](const auto& x, const auto&y){
                return x[1] < y[1];
                });
        sort(a.begin() + mid, a.begin() + r, [&](const auto& x, const auto&y){
                return x[1] < y[1];
                });

        vector<int> suf(r - l);
        for(int i = r - 1; i >= l; -- i){
            suf[i - l] = a[i][2];
            if (i < r - 1)
                suf[i - l] = max(suf[i - l], suf[i - l + 1]);
        }
        int pos = mid;
        for(int i = l; i < mid; ++ i){
            while(pos < r && a[pos][1] <= a[i][1])
                ++ pos;
            if (pos == r)
                break;
            if (suf[pos - l] > a[i][2])
                ok = 1;
        }
    };
    solve(0, n);
    if (ok)
        cout << "Yes" << '\n';
    else 
        cout << "No" << '\n';

    return 0;
}



G - Ban Permutation (abc309 G)

题目大意

问关于\(n\)的排列的数量,满足 \(\forall i \in [1,n], |p_i - i| \geq x\)。

解题思路

<++>

神奇的代码



Ex - Simple Path Counting Problem (abc309 Ex)

题目大意

<++>

解题思路

<++>

神奇的代码



标签:AtCoder,cout,Beginner,309,int,auto,cin,mid,using
From: https://www.cnblogs.com/Lanly/p/17538041.html

相关文章

  • AtCoder Grand Contest 058 D Yet Another ABC String
    洛谷传送门AtCoder传送门OrzH6_6Q,感谢他给我们带来了这道容斥好题。这个东西看起来很不好搞。可以尝试容斥。但是我们要容斥啥?钦定ABC不出现,其他任意?感觉还是很难算。观察不合法子串,发现它们很有特点。如果我们钦定\(\texttt{A}\)为\(0\),\(\texttt{B}\)为\(1\),\(\te......
  • AtCoder Beginner Contest 308 题解
    https://atcoder.jp/contests/abc308/tasks_printA-NewScheme过水已隐藏。#include<bits/stdc++.h>#include<ext/pb_ds/assoc_container.hpp>#include<ext/pb_ds/tree_policy.hpp>#include<ext/pb_ds/hash_policy.hpp>usingnamespacestd;u......
  • python: PyQt5 beginner
     fromPyQt5.QtWidgetsimportQWidget,QApplication,QMainWindow,QLabel,QPushButtonfromPyQt5importQtCore,QtGuiimportsysimportosdefclick():print("HyButtonisclicked!")#Pressthegreenbuttonintheguttertorunthescri......
  • AtCoder Beginner Contest 264 ABCDE
    AtCoderBeginnerContest264A-"atcoder".substr()ProblemStatement题意:截取字符串atcoder的[L,R]一段并输出。Solution题解:用string.substr直接写#include<bits/stdc++.h>usingnamespacestd;intmain(){ strings="?atcoder"; intl,r; cin&......
  • AtCoder Regular Contest 163
    A只需暴力判断能否分成两部分即可。时间复杂度\(\mathcal{O}(n^2)\)。B肯定是选值域连续的一段数操作,排序后枚举区间即可。时间复杂度\(\mathcal{O}(n\logn)\)。C场上降智了15min,我是什么shaber啊。注意到\(1=\frac1n+\sum_{i<n}\frac{1}{i(i+1)}\),但......
  • AtCoder Beginner Contest 304
    A:1#include<cstdio>2#include<cstring>3#include<algorithm>4#include<iostream>5#include<string>6#include<vector>7#include<stack>8#include<bitset>9#include<cstdlib>10#include......
  • AtCoder Beginner Contest 308 - E
    题目链接:abc308前四题简单就不放了E-MEX阿巴阿巴,比赛的时候想复杂了,一直在想怎么快速的统计27种mex的情况,啊,前面对后面的影响等等等,反正就是想复杂了现在再想想,看了官方题解,从'E'出发,统计其前后各3种数字的个数,再用mex函数判答案,\(O(n)\)即可!剩下的见代码吧,做完之后发现,没......
  • Atcoder Beginer Contest 306 D ~ E
    vp中途突然拉肚子>_<D-PoisonousFull-CourseD-PoisonousFull-Course(atcoder.jp)题意一个人初始是健康的,现在有n道菜摆在他面前,每道菜都有自已的美味值,但是有些菜是有毒的,有些菜是无毒的。如果他在健康状态吃了有毒的菜就会变成中毒状态,如果他在中毒状态吃了无毒的菜就......
  • AtCoder Regular Contest 163
    Preface补题,这场比赛的时候被拉去开科研组会了,所以就没现场打了这两天军训在伤病连划水,白天可以好好想题目舒服的一批这场D题确实很妙,需要一些竞赛图相关的知识才能想到转化,不过也算是学到一个重要trick了吧A-DivideString显然只要考虑能否分成两个串即可,首先如果存在\(i......
  • AtCoder Regular Contest 163 D Sum of SCC
    洛谷传送门AtCoder传送门怎么连这种相对传统的计数也不会……考虑换种方式描述强连通分量个数。考虑竞赛图缩点后存在一条极长的链,因此转化为把缩完点后的链劈成左右两个集合,使得左边集合不为空的方案数。于是我们现在只要统计点集\(A,B\)数量,满足\(A\ne\varnothing,A......