Educational Codeforces Round 77 (Rated for Div. 2)

传送门

感觉最近写代码的状态有点迷...还好这次最后两分钟过了D,不然就掉分了QAQ。

A. Heating

签到。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2019/11/27 21:53:49
 */
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
void run(){
    int c, sum; cin >> c >> sum;
    int d = max(sum / c, 1), r = sum % c;
    int ans = 0;
    if(r == 0) {
        ans = d * d * c;
    } else {
        for(int i = 1;; i++) {
            ans += d * d;
            --c; sum -= d;
            if(sum == 0) break;
            if(sum % c == 0) {
                d = sum / c;
                ans += c * d * d;
                break;
            }
        }   
    }
    cout << ans << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T;
    while(T--) run();
    return 0;
}

B. Obtain Two Zeroes

分情况,列一下方程找关系即可。

Code
/*
 * Author:  heyuhhh
 * Created Time:  2019/11/27 22:03:54
 */
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
void run(){
    int x, y; cin >> x >> y;
    if(x > y) swap(x, y);
    int f = 0;
    if(2 * y - x >= 0 && (2 * y - x) % 3 == 0) {
        int t = (2 * y - x) / 3;
        if(x >= t && y >= 2 * t) f = 1;
    }
    if(2 * x - y >= 0 && (2 * x - y) % 3 == 0) {
        int t = (2 * x - y) / 3;
        if(x >= 2 * t && y >= t) f = 1;
    }
    if(f) cout << "YES" << '\n';
    else cout << "NO" << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T;
    while(T--) run();
    return 0;
}

C. Infinite Fence

题意:
现在有无穷多个砖块排成一行,现在从\(0\)开始,每\(r\)个涂一种颜色,每\(b\)个涂一种颜色。形式化地说,就是\(0,r,2r,\cdots\)以及\(0,b,2r,\cdots\)这些砖块各涂一种颜色。
如果存在一个位置\(x\),满足\(r|x,b|x\),那么\(x\)这个位置可以选择一种颜色去涂。
现在有多组询问,每组询问回答是否有颜色相同且连续的多个砖块,其个数小于\(k\)

思路:

  • 显然我们只需要考虑\(0\)~\(lcm(r,b)\)即可。
  • 不妨设\(r<b\),那么\(q=b\% r\),也就相当于\(b\)每涂一次,只会在长度为\(r\)的区间中前进\(q\)(不考虑中间经过的完整的区间)。
  • 显然,连续的砖块个数最多的情况,就是存在\(x,y\),使得\(|xr-yb|\)最小,也就是说某一次涂完\(b\)之后,这时的位置离\(r\)的倍数最近,那么下一次涂就可以尽可能远。
  • \(p=r\% q\),之后即可确定出起点,然后找到终点,统计一下中间个数即可。

好像这个有个什么结论,不过这种思路也不是很复杂~
细节见代码:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2019/11/27 22:33:59
 */
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
ll r, b, k;
 
void run(){
    cin >> r >> b >> k;
    if(r > b) swap(r, b);
    if(b % r == 0) {
        int d = b / r - 1;
        if(d >= k) {
            cout << "REBEL" << '\n';   
        } else cout << "OBEY" << '\n';
    } else {
        int q = b % r;
        int p = r % q;
        ll s;
        if(p == 0) {
            s = r - q;
        } else s = r - p;
        ll e = s + b;
        int cnt = e / r - (s - 1) / r;
        if(e % r == 0) --cnt;
        if(cnt >= k) cout << "REBEL" << '\n';
        else cout << "OBEY" << '\n';
    }
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T;
    while(T--) run();
    return 0;
}

D. A Game with Traps

题意:
现在有一条长度为\(n\)的战线,你要带领一些士兵从\(0\)\(n+1\)
中间的某些位置可能存在陷阱,其位置为\(l_i\),深度为\(d_i\)。某些士兵有其敏捷度\(a_i\),若\(a_i<d_i\),他就会阵亡。
但是你,不会受到陷阱的影响,相反若对于某个\(l_i\),走到\(r_i\),能够拆除这个陷阱。
你当前可以进行如下操作:

  • 花费一秒的时间,独立从\(x\)走到\(x-1\)\(x+1\)
  • 花费一秒的时间,带领士兵从\(x\)走到\(x+1\),此时你也要在\(x+1\)
  • 进行拆除陷阱的操作,不消耗时间。

问在\(t\)秒内,能够带领最多多少的士兵走到\(n+1\)的位置。

思路:
这个题题意稍微有点绕,但是思路不是很难。
很显然的我们可以二分答案。
然后直接模拟这个过程,贪心往前走就行。关键就是通过一个变量\(rbound\)来记录你走到右边的最远位置,因为拆除陷阱是,肯定是能拆的一次性拆完。我们利用\(rbound\)来统计贡献就行。
细节见代码:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2019/11/27 22:56:01
 */
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 2e5 + 5;
 
int m, n, k, t;
int a[N];
vector <pii> v[N];
 
bool chk(int x) {
    ll c = 0;
    int rb = 0;
    int low = a[m - x + 1];
    for(int i = 0; i <= n; i++) {
        rb = max(rb, i);
        if(sz(v[i + 1]) > 0) {
            for(int j = 0; j < sz(v[i + 1]); j++) {
                pii now = v[i + 1][j];
                if(now.fi <= low || now.se <= rb) {} 
                else {
                    c += 2 * (ll)(now.se - rb);
                    rb = now.se; 
                }
            }
        }
        if(++c > (ll)t) return false;
    }
    return true;
}
 
void run(){
    for(int i = 1; i <= m; i++) cin >> a[i];
    sort(a + 1, a + m + 1);
    for(int i = 1; i <= k; i++) {
        int l, r, d; cin >> l >> r >> d;
        v[l].push_back(MP(d, r));   
    }
    for(int i = 1; i <= n; i++) if(sz(v[i])) {
        sort(v[i].begin(), v[i].end(), [&](pii A, pii B) {
             if(A.fi != B.fi) return A.fi > B.fi;
             else return A.se > B.se; });   
    }
    int l = 1, r = m + 1, mid;
    while(l < r) {
        mid = (l + r) >> 1;
        if(chk(mid)) l = mid + 1;
        else r = mid;   
    }
    cout << l - 1 << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    while(cin >> m >> n >> k >> t) run();
    return 0;
}

E. Tournament

题意:
现在有\(n\)个选手参加比赛,你的朋友也在里面。
每个选手有一个力量值\(i\),显然在两两的比赛中力量值大的会胜出。
比赛的规则就是每一轮两两比赛,最后胜者进入下一轮,然后重复此过程直至某一人胜出。
你的朋友很可能GG,但是你可以贿赂那些人,贿赂的代价为\(a_i\)
问最后使得你的朋友胜出的最小代价是多少。

思路:
感觉很有意思的一个题,也不是很好想(菜就对了)。
显然如果我们倒着来考虑整个比赛的过程,那么会形成一颗满二叉树。
第二层有两个结点,一个结点是你的朋友,另一个结点是你的对手。那么可以当作你的对手在之前KO了\(\frac{n}{2}\)个人,而此时你必须贿赂你的对手来获得胜利(如果他力量比你低,那代价为\(0\),因为此时的对手肯定是最后一个人)。
同理,之后到了第三层,你朋友的对手会打败\(\frac{n}{4}\)个人...然后依次类推。
之后有一个引理:

  • 若当前是第\(x\)层,那么你会遇到\(x-1\)个对手,所有的对手会打败\(tot=\frac{n}{2}+\cdots+\frac{n}{2^{x-1}}\)个人。那么,下一轮你能够从后面\(tot+1\)个位置任选一个人(当然不能选过)当作你的对手。

证明的话就是对于每一个规模的问题,你肯定要贿赂当前的最强者。又因为可以任意安排策略,所以你一定能够在后面(力量值比较大的部分)选择你的对手。同时每一个对手又能不断增加击败人数...然后yy一下就有了。

如果能将树的思路和这个引理想到并且想清楚了。那么用一个小根堆模拟一下就行。
还是挺巧妙的hhh思路转换一下就出来了。
代码如下:

Code
/*
 * Author:  heyuhhh
 * Created Time:  2019/11/28 22:15:03
 */
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <iomanip>
#include <queue>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
  #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
  void err() { std::cout << '\n'; }
  template<typename T, typename...Args>
  void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
  #define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5;
 
int n;
int a[N];
 
void run(){
    for(int i = 1; i <= n; i++) cin >> a[i];
    reverse(a + 1, a + n + 1);
    priority_queue <int, vector<int>, greater<int> > q;
    int now = 1;
    q.push(a[now++]);
    ll ans = 0;
    int win = n / 2;
    while(!q.empty()) {
        int cur = q.top(); q.pop();
        if(cur == -1) break;
        ans += cur;
        for(int i = 1; i <= win; i++) q.push(a[now++]);
        win /= 2;
    }
    cout << ans << '\n';
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    while(cin >> n) run();
    return 0;
}
posted @ 2019-11-29 09:06  heyuhhh  阅读(339)  评论(0编辑  收藏  举报