AtCoder Beginner Contest 192

AtCoder Beginner Contest 192

D - Base n

题意: 给出n,m,设n中最大的数字为x,问在不同的进制(bas>x)下,小于等于m的不同的数有多少个

题解: 二分。 发现进制其实有单调性,进制越大,得到的值越大。那么我直接二分。然后需要特判下一位的情况。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
#define int LL
inline int read() {
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

int const MAXN = 2e5 + 10;
int n, T;
string s;

LL m;
LL tmp[MAXN];
LL ss[MAXN];
bool check(LL mid) {
    LL mm = m;
    int cnt = 0;
    while (mm) {
        tmp[cnt++] = mm % mid;
        mm /= mid;
        if (cnt > 70) break;
    }
    if (cnt > s.size())
        return 1;
    else if (cnt < s.size())
        return 0;
    else {
        for (int i = cnt - 1; i >= 0; i--) {
            if (tmp[i] > ss[i]) return 1;
            if (tmp[i] < ss[i]) return 0;
        }
    }
    return 1;
}

signed main() {
    cin >> s;
    for (int i = 0; i < s.size(); i++) {
        ss[(int)s.size() - i - 1] = s[i] - '0';
    }
    cin >> m;
    LL maxn = 0;
    for (int i = 0; i < s.size(); i++) {
        maxn = max(maxn, (LL)(s[i] - '0'));
    }
    if (s.size() == 1) {
        if (s[0] - '0' <= m)
            cout << 1;
        else
            cout << 0;
        return 0;
    }
    /*
    if (check(maxn + 1) == false)
    {
        cout << 0;
        return 0;
    }
*/
    LL l = maxn , r = 1e18;
    while (l < r) {
        LL mid = (l + r + 1) >> 1;  // 区别:有无加1
        if (check(mid))
            l = mid;  //区别
        else
            r = mid - 1;  //区别
    }
    cout << l - maxn << endl;
    return 0;
}

E - Train

题意: 有n座城市之间有m条双向高铁线路连接,第i条高铁线路会在ki的倍数的时刻进站(包括0倍),花费ti的时间到另一个端点,问S到T的最短时间。 1 < = N < = 1 e 5 1<=N<=1e5 1<=N<=1e5

题解: 最短路变形。一辆人如果到达u点,然而这时高铁没有发车,那么这个人必然等到一发车就走,因此只需要找到大于等于u点时刻的最早发车时刻,以这个时刻来作为u点最短路来更新邻边。

代码:

#include<bits/stdc++.h>

using namespace std;

#define int long long

inline int read() {
    int s = 0, w = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}

typedef pair<int, int> PII;
int const N = 4e5 + 10;
int e[N], ne[N], w[N], h[N], idx, n, m, dis[N], st[N], x, y, s[N];

void add(int a, int b, int c, int d) {
    e[idx] = b, w[idx] = c, s[idx] = d, ne[idx] = h[a], h[a] = idx++;
}

// 堆优化版dijksyta
int dijkstra() {
    memset(dis, 0x3f, sizeof dis);  // 初始化距离为无穷
    priority_queue<PII, vector<PII>, greater<PII> > q;  // 定义一个按照距离从小到大排序的优先队列,第一维:距离,第二维:点
    dis[x] = 0;  // 一开始源点距离为0
    q.push({0, x});  // 把源点信息放入队列
    while (q.size()) {  // 每个点只出入队列一次
        auto t = q.top();
        q.pop();
        
        int distance = t.first, ver = t.second;  // 最小距离和相对应的点
        if (st[ver]) continue;  // 这个操作保证每个点只出入队一次,因为队列里面可能会出现{dis1[3], 3}, {dis2[3], 3}的情况,这样保证dis1[3]<dis2[3]时,3号点只进出入队一次
        st[ver] = 1;  // 标记,因为dijkstra的贪心策略保证每个点只需要进出队一次
        
        for (int i = h[ver]; ~i; i = ne[i]) {  // 遍历ver的邻接点
            int j = e[i];
            //  d[e[i]]=(d[u]+s[i]-1)/s[i]*s[i]+w[i];
            if (dis[j] > w[i] + (int)ceil((double)distance / s[i]) * s[i]) {
                dis[j] = w[i] + (int)ceil((double)distance / s[i]) * s[i];
                q.push({dis[j], j});  // 这里不需要判断st,因为一旦更新发现更小必须放入队列
            }
        }
    }
    return dis[y] != 0x3f3f3f3f3f3f3f3f? dis[y]: -1;
}

signed main() {
    n = read(), m = read(), x = read(), y = read();
    memset(h, -1, sizeof h);  
    for (int i = 1, a, b, c, d; i <= m; ++i) {  // 读入m条边
        a = read(), b = read(), c = read(), d = read();
        add(a, b, c, d), add(b, a, c, d);
    }
    if (x == y) {
        cout <<0 << endl;
        return 0;
    }
    cout << dijkstra();
    return 0;
}

F - Potion

题意: 有n个数,你可以在第0个时刻条任意多个数出来,并让计数器加上他们的和,设你挑了k个数,那么之后每个时刻计数器都会加k,你要最小化计数器恰好到x的时间。 1 < = n < = 100 , 1 < = a i < = 1 e 7 , 1 0 9 < = X < = 1 0 1 8 1<=n<=100,1<=a_i<=1e7,10^9<=X<=10^18 1<=n<=100,1<=ai<=1e7,109<=X<=1018

题解: 01背包变形。看到x其实很小,那么直接枚举x,然后不断更新答案。设挑了s个数出来,那么为了满足题意,他们的和对s取余一定等于x%s。

那么考虑dp,f(i,j,k)表示前i个数挑了j个数出来,对s取余结果为k,枚举第i个数是否取即可。具体看:https://www.cnblogs.com/szmssf/p/14423803.html

代码: 贴上大佬代码

#include <bits/stdc++.h>

#define N 105

#define int long long

using namespace std;

int n, f[N][N][N], a[N], X, ans = 1e18;
signed main() {
    cin >> n >> X;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int S = 1; S <= n; S++) {
        memset(f, -0x3f, sizeof(f));
        f[0][0][0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= S; j++) {
                for (int k = 0; k <= n; k++) {
                    f[i][j][k] = max(f[i - 1][j][k], f[i - 1][j - 1][((k - a[i]) % S + S) % S] + a[i]);
                }
            }
        }
        if (f[n][S][X % S] < 0)
            continue;
        else
            ans = min(ans, (X - f[n][S][X % S]) / S);
    }
    printf("%lld\n", ans);
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值