2024 暑假友谊赛-热身1(7.11)

AtCoder abc079_d

思路:floyd跑最短路(赛时把循环顺序记错了🤡)

#include <bits/stdc++.h>

using namespace std;

#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;

int c[10][10];
void solve() {
    int h, w;
    cin >> h >> w;
    for (int i = 0; i <= 9; ++i) {
        for (int j = 0; j <= 9; ++j) {
            cin >> c[i][j];
        }
    }
    for (int k = 0; k <= 9; ++k) {
        for (int i = 0; i <= 9; ++i) {
            for (int j = 0; j <= 9; ++j) {
                if (c[i][k] + c[k][j] < c[i][j]) {
                    c[i][j] = c[i][k] + c[k][j];
                }
            }
        }
    }

    int ans = 0;
    for (int i = 1; i <= h; ++i) {
        for (int j = 1; j <= w; ++j) {
            int x;
            cin >> x;
            if (x != -1) ans += c[x][1];
        }
    }
    cout << ans;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T = 1;
    while (T--) {
        solve();
    }
    return 0;
}

AtCoder arc100_a

思路:求中位数的变形,排个序取中位数就行了

#include <bits/stdc++.h>

using namespace std;

#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;


void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    int sum = 0;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        a[i] -= i;
        sum += a[i];
    }
    sort(a.begin() + 1, a.end());
    int x = n / 2, ans = LLONG_MAX;
    for (int i = max(1ll, x - 100); i <= min(n, x + 100); ++i) {
        int b = a[i], cnt = 0;
        for (int j = 1; j <= n; ++j) {
            cnt += abs(a[j] - b);
        }
        ans = min(ans, cnt);
    }
    cout << ans;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T = 1;
    while (T--) {
        solve();
    }
    return 0;
}

AtCoder arc099_a

思路:给的是个排列,最小值就是1,就是要全部变为1,模拟过程求数量

#include <bits/stdc++.h>

using namespace std;

#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;


void solve() {
    int n, k;
    cin >> n >> k;
    vector<int> a(n + 5);
    int pos;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        if (a[i] == 1) pos = i;
    }
    int l = 1, cnt = 1;
    while (l + k <= n) {
        cnt ++, l += k - 1;
    }
    cout << cnt;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T = 1;
    while (T--) {
        solve();
    }
    return 0;
}

AtCoder arc100_b

思路:这里用的是二分,先确定第二个分割点,左右两边在分别求一个分割点

要使得差最小,应该是保证分割点两边的和是尽可能接近的,去二分前缀和中接近sum/2的位置,枚举附近的数为分割点,取最小值

 

确定了第二个分割点后,也可以用双指针维护第一和第三个分割点

#include <bits/stdc++.h>

using namespace std;

#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;


void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1), sum(n + 1);
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        sum[i] = sum[i - 1] + a[i];
    }

    int ans = LLONG_MAX;
    for (int i = 2; i + 2 <= n; ++i) {
//        cout << "i= "<< i << ':' << '\n';

        int suml = sum[i], sumr = sum[n] - sum[i];
        auto pl = std::lower_bound(sum.begin(), sum.end(), suml / 2);
        int pll = pl - sum.begin();
//        cout << "p:  " << pll << '\n';
        int dl = LLONG_MAX, tl;
        for (int j = max(1ll, pll - 3); j <= pll + 3 && j + 1 <= i; ++j) {
//            cout << " j: " << j << "    ";
//            cout << sum[j] << ' ' << sum[i] - sum[j] << '\n';
            int dd = abs(sum[j] - (sum[i] - sum[j]));
            if (dd < dl) {
                tl = j;
                dl = dd;
            }
        }
        auto pr = std::lower_bound(sum.begin(), sum.end(), sum[i] + (sumr / 2));
        int prr = pr - sum.begin();
        int dr = LLONG_MAX, tr;
        for (int j = max(i + 1, prr - 3); j <= prr + 3 && j + 1 <= n; ++j) {
            int dd = abs(sum[j] - sum[i] - (sum[n] - sum[j]));
            if (dd < dr) {
                tr = j;
                dr = dd;
            }
        }
        int ma = max({sum[tl], sum[i] - sum[tl], sum[tr] - sum[i], sum[n] - sum[tr]});
        int mi = min({sum[tl], sum[i] - sum[tl], sum[tr] - sum[i], sum[n] - sum[tr]});
        ans = min(ans, ma - mi);
    }
    cout << ans;

}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T = 1;
    while (T--) {
        solve();
    }
    return 0;
}

CodeForces 1808C

思路:

数位dp的思想进行搜索

首先是确定当前位,以及这一位是否是左右边界,以及当前最小最大值

然后一位一位的搜

 

还有一个思路就是暴力枚举最小最大值,判断是否存在范围内的数满足条件

在已知最小最大值的情况下,如何判断当前位上的数是否可行:

不大于右边界:将当前位后的所有数变成最小值后不大于有边界

不小于左边界:将当前位后的所有数变成最大值后不小于左边界

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

#define int long long
#define PII pair<int, int>

const int N = 1e5 + 5, mod = 998244353;



void solve() {
    string ls, rs;
    cin >> ls >> rs;
    int l, r;
    l = stoll(ls), r= stoll(rs);
    int len = 10;
    if (l == r) {
        cout << l << '\n';
        return ;
    }
    if (ls.size() != rs.size()) {
        for (int i = 0; i < ls.size(); ++i) cout << '9';
        cout << '\n';
        return;
    }
    string ans;
    auto P = [=] (int mi, int ma) -> pair<int, string>{
        if (mi == ma && mi == 0) return {0, ""};
        string t(ls.size(), '0');
        for (int i = 0; i < t.size(); ++i) {
            bool is = false;
            for (int j = mi; j <= ma; ++j) {
                string tr = t;
                tr[i] = j + '0';
                for (int k = i + 1; k < t.size(); ++k) tr[k] = mi + '0';
                if (tr > rs) {
                    return {0, ""};
                }
                string tl = t;
                tl[i] = j + '0';
                for (int k = i + 1; k < t.size(); ++k) tl[k] = ma + '0';
                if (tl < ls) continue;
                is = true;
                t[i] = j + '0';
                break;
            }
            if (!is) return {0, ""};
        }
        return {1, t};
    };
    for (int i = 0; i < 10; ++i) {
        for (int j = i; j < 10; ++j) {
            auto [ok, s] = P(i, j);
            if (ok && abs(i - j) < len) {
                len = abs(i - j), ans = s;
            }
        }
    }
    cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(0),cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}
#include<bits/stdc++.h>
using namespace std;

#define int long long
#define PII pair<int, int>

const int N = 1e5 + 5, mod = 998244353;

string ls, rs;
int l, r, n;
string ans, te;
int len = 10;

void dfs(int now, int st, int ed, int mi, int ma) {
    if (ma - mi >= len) return;
    if (now == n - 1) {
        len = ma - mi;
        ans = te;
        return ;
    }

    char ll = '0', rr = '9';
    if (st && ed) {
        ll = ls[now + 1], rr = rs[now + 1];
    } else if (st) {
        ll = ls[now + 1];
    } else if (ed) {
        rr = rs[now + 1];
    }
    for (int i = ll; i <= rr; ++i) {
        te.push_back(i);
        dfs(now + 1, i == ll && st, i == rr && ed, min(mi, i - '0'), max(ma, i - '0'));
        te.pop_back();
    }

}

void solve() {
    te.clear();
    cin >> ls >> rs;
    l = stoll(ls), r= stoll(rs);
    n = ls.size();
    ans = ls, len = 10;
    if (l == r) {
        cout << l << '\n';
        return ;
    }
    if (ls.size() != rs.size()) {
        for (int i = 0; i < ls.size(); ++i) cout << '9';
        cout << '\n';
        return;
    }
    for (int i = ls[0]; i <= rs[0]; ++i) {
        te.push_back(i);
        dfs(0, i == ls[0], i == rs[0],i - '0', i - '0');
        te.pop_back();
    }
    cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(0),cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}

CodeForces 1547E

思路:由式子可以看出i的温度是和i-1和i+1有关系的

当i>aj时,式子为tj-aj+i,i的温度比i-1的温度多1;

当i<=aj时,式子为tj+aj-i,i的温度比i+1的温度多1

直接先标记每个a[j]位置的温度,左右分别两次传递所有a[j]带来的温度的最小值

下面的代码是维护的前缀tj-aj和后缀tj+aj

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define PII pair<int, int>
const int N = 2e5 + 5;

struct E {
    int a, t;
//    bool operator<(const E&e)const {
//        a < e.a;
//    }
};
bool cmp(E x, E y) {
    return x.a < y.a;
}

void solve() {
    int n, k;
    cin >> n >> k;
    vector<E> ve(k + 1);
    vector<int> pos(n + 1);
    for (int i = 1; i <= k; ++i) cin >> ve[i].a;

    for (int i = 1; i <= k; ++i) cin >> ve[i].t;
    sort(ve.begin() + 1, ve.end(), cmp);
    for (int i = 1; i <= k; ++i) {
        pos[ve[i].a] = i;
    }
    vector<int> l(n + 1, 1e16), r(n + 2, 1e16);
    for (int i = 1; i <= n; ++i) {
        l[i] = l[i - 1];
        if (pos[i]) l[i] = min(l[i], ve[pos[i]].t - ve[pos[i]].a);
    }
    for (int i = n; i >= 1; --i) {
        r[i] = r[i + 1];
        if (pos[i]) r[i] = min(r[i], ve[pos[i]].t + ve[pos[i]].a);
    }

    for (int i = 1; i <= n; ++i) {
        cout << min(l[i - 1] + i, r[i] - i) << ' ';
    }
    cout << '\n';

}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T = 1;
    cin >> T;
    while (T --) {
        solve();
    }

    return 0;
}

CodeForces 1107C

思路:用单调队列维护下当前连续相同字符的数,直接统计

#include <bits/stdc++.h>

using namespace std;

#define int long long
//#define double long double
#define PII pair<int, int>
const int N = 1e6 + 55, mod = 1e9 + 7;


void solve() {
    int n, k;
    cin >> n >> k;
    vector<int> ve(n + 1);
    for (int i = 1; i <= n; ++i) cin >> ve[i];
    string s;
    cin >> s;
    priority_queue<int> q;
    s = ' ' + s + ' ';
    int ans = 0;
    char pre = ' ';
    for (int i = 1; i <= n + 1; ++i) {
        if (s[i] != pre) {
            int cnt = k;
            while (cnt -- && q.size()) {
                ans += q.top();
                q.pop();
            }
            while (q.size()) q.pop();
        }
        q.push(ve[i]);
        pre = s[i];
    }
    cout << ans;

}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int T = 1;
    while (T--) {
        solve();
    }
    return 0;
}

AtCoder arc102_b

思路:

要求用不超过20个点,60条边,且L的范围为1e6

220刚好为1e6,就可以猜测跟二进制有关

 

对于最简单的情况,构建0~2k - 1的路径:

构造一条链,每个点按顺序表示二进制上的每一位,点i表示二进制上第i - 1位。

相邻的点之间连2条边,一条表示在二进制上为0(即边权为0),另一条表示在二进制上为1(即边权为2i

 

现在的问题是要恰好有L条路径,如果按上面方法构造,路径数一定是2k - 1条,下面考虑用添加一些特殊边来构造

 

首先看一个二进制数10101,有5位,且最高位在4

若把第4位(最高位)变成0,把它后面都变成1,即变成01111,10101一定是包含0~01111的数的,且0~01111就可以直接用上述方法直接构造

对于剩下的10000~10101,等价于10000 + (0~00101),其中0~00101又可以同理的把第2位(最高位)变成0,把它后面的都变成1,即变成00011,所以0~00101等价于0~00011 和 00100~00101的并集,而其中的00100~00101又可以等价于00100 + (0~00001),0~00001也可以直接用上述方法直接构造

知道了原理进行建边,根据上述的构造方法,走到第i个点所包含的路径为0~2i-2,将i进行连边,相当于得到了权值为0~2i-2的路径,所以将每次等价出的2k-1通过连边来获取即可

 

struct E {
    int u, v, w;
};

void solve() {
    int L;
    cin >> L;
    vector<E> g;
    int n, ma;
    //  n表示L二进制下的位数
    //  ma表示L的最高位
    for (int i = 20; i >= 0; --i) {
        if ((L >> i) & 1) {
            ma = i;
            n = i + 1;
            break;
        }
    }

    //  先构造0 ~ 2^(ma - 1) - 1的路径
    //  i节点连的边表示二进制第i位是0或1(从1开始)
    for (int i = 1; i <= ma; ++i) {
        g.push_back({i, i + 1, 1 << i - 1});
        g.push_back({i, i + 1, 0});
    }

    int now = 1 << ma;  //  当前边权
    for (int i = n - 1; i >= 1; --i) {
        if ((L >> i - 1) & 1) {
            g.push_back({i, n, now});
            //  节点i的路径有 0 ~ 2^(i - 2)
            now += 1 << i - 1;
        }
    }

    cout << n << ' ' << g.size() << '\n';
    for (auto [u, v, w]:g) cout << u << ' ' << v << ' ' << w << '\n';

}

AtCoder abc297_g

posted @ 2024-07-13 00:07  bible_w  阅读(42)  评论(0编辑  收藏  举报