AtCoder Beginner Contest 340

前面两道阅读理解直接跳过

C - Divide and Divide

大意

黑板上有一个数n

执行下列操作,直到黑板上的数全为1:

  • 选择一个不小于2的整数x,擦掉。
  • 写下\lfloor \frac{x}{2} \rfloor\lceil \frac{x}{2} \rceil
  • 需要x的代价。

当不能继续操作时,总代价是多少?

思路

定义dp_i表示黑板上初始出现数字i的代价,则有dp_i=dp_{\lfloor \frac{x}{2}\rfloor}+dp_{\lceil \frac{x}{2}\rceil} + x

但是数很大,不能递推,需要记忆化搜索(有用的总状态数不会超过2\log n)。

使用map进行记忆化即可。

代码

#include <iostream>
#include <unordered_map>
using namespace std;

typedef long long ll;
unordered_map<ll, ll> dp;

ll dfs(ll x) {
    if (x < 2) return 0;
    if (dp.count(x)) return dp[x];
    return dp[x] = x + dfs(x / 2) + dfs((x + 1) / 2);
}

int main() {
    ll n;
    cin >> n;
    cout << dfs(n) << endl;
    return 0;
}

D - Super Takahashi Bros.

大意

n个关卡,初始只能玩第1关。

对于第i关卡,有两种通关方式:

  • 花费A_i时间打完,跳到第i+1关。
  • 花费B_i时间打完,跳到第X_i关。

思路

转化为有向图,对于第i关卡,

  • 在点i和点i+1之间,建一条权值为A_i的边。
  • 在点i和点X_i之间,建一条权值为B_i的边。

最后跑一遍从1n的最短路即可。

代码

#include <iostream>
#include <queue>
#include <utility>
#include <cstring>
using namespace std;

const int N = 2e5 + 9;
typedef long long ll;
typedef pair<ll, ll> pll;
ll n, a[N], b[N], x[N], dis[N];
bool vis[N];
priority_queue<pll, vector<pll>, greater<pll>> q;

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    for (ll i = 1; i <= n - 1; i++) cin >> a[i] >> b[i] >> x[i];
    memset(dis, 0x3f, sizeof dis);
    dis[1] = 0;
    q.push({ 0, 1 });
    while (q.size()) {
        auto t = q.top();
        q.pop();
        ll u = t.second;
        if (vis[u]) continue;
        vis[u] = true;

        ll v = u + 1;
        if (dis[v] > dis[u] + a[u]) {
            dis[v] = dis[u] + a[u];
            q.push({ dis[v], v });
        }
        v = x[u];
        if (dis[v] > dis[u] + b[u]) {
            dis[v] = dis[u] + b[u];
            q.push({ dis[v], v });
        }
    }
    cout << dis[n] << endl;
    return 0;
}

E - Mancala 2

大意

n个盒子,第i个盒子有A_i个球。

依次执行以下操作共m次:

  • i次操作,将第B_i个盒子的所有球均分,多余的球从第B_i+1个盒子开始依次放一个。
  • 如果处理到最后一个盒子还没放完,从第1个盒子开始继续放。

问最后每个盒子的球数量。

思路

假设被取出球的盒子为x,取出了y个球,则放球时相当于:

  • 先给每个盒子\lfloor \frac{y}{n} \rfloor个球
  • 把剩下y \mod n个球依次放入对应盒子。(注意分成两个部分算)

这是一个单点查询单点修改区间修改的操作,使用树状数组+差分维护即可。

代码

#include<iostream>
#include<vector>
using namespace std;
#define int long long

template<class T>
struct fenwick{
    vector<T> tr;
    int n;
    fenwick(int _n): n(_n){
        tr.resize(n + 1);
    }

    void add(int a, T b){
        while(a <= n){
            tr[a] += b;
            a += (a & -a);
        }
    }

    T ask(int a){
        T res{};
        while(a){
            res += tr[a];
            a -= (a & -a);
        }
        return res;
    }
};


signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int n, m;
    cin >> n >> m;
    fenwick<int> fwk(n);

    auto add = [&](int l, int r, int d){
        fwk.add(l, d); fwk.add(r + 1, -d);
    };

    for(int i = 1; i <= n; i++){
        int x; cin >> x;
        add(i, i, x);
    }
    while(m--){
        int x; cin >> x; x++;

        int y = fwk.ask(x);
        add(x, x, -y), add(1, n, y / n);
        add(x+1, min(n, x + y % n), 1);
        if(x + y % n > n) add(1, y % n - (n - x), 1);
    }
    for(int i = 1; i <= n; i++) cout << fwk.ask(i) << " ";
    return 0;
}

F - S = 1

大意

给定点(x,y),求一整数点(a,b),使得(0,0),(a,b),(x,y)形成的三角形的面积为1

思路

画一张图:

明显这个三角形面积等于这个长方形的面积减去周边三个小三角形的面积。

S=ay-\dfrac{ab}{2}-\dfrac{xy}{2}-\dfrac{(a-x)(y-b)}{2}

化简可得S=\dfrac{ay-bx}{2},但由于要考虑第二象限,所以S应为|\dfrac{ay-bx}{2}|

使用扩展欧几里德求|ay-bx|=2的一组特解即可。

无解情况:如果2 \mod \gcd(a,b) \ne 0,那么无解。

代码

#include<iostream>
using namespace std;
#define int long long

int exgcd(int a, int b, int &x, int &y){
    if (!b){
        x = 1; y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}


signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int x, y, a, b;
    cin >> a >> b;
    int d = exgcd(a, b, y, x);
    x = -x;
    if(2 % d) cout << -1 << endl;
    else{
        x = x * (2 / d);
        y = y * (2 / d);
        cout << x << ' ' << y << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值