$$ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Self-defined math definitions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Math symbol commands \newcommand{\intd}{\,{\rm d}} % Symbol 'd' used in integration, such as 'dx' \newcommand{\diff}{{\rm d}} % Symbol 'd' used in differentiation ... $$

Educational Codeforces Round 47 (Rated for Div

Educational Codeforces Round 47 (Rated for Div. 2)

A. Game Shopping

​ 暴力模拟即可

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define inf INT32_MAX
#define PII pair<int,int>
#define endl '\n'

inline void solve() {
    int n, m;
    cin >> n >> m;
    vector<int> arr(n + 1);
    for (int i = 1; i <= n; i++)cin >> arr[i];
    queue<int> q;
    for (int i = 1; i <= m; i++) {
        int x;
        cin >> x;
        q.push(x);
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (q.empty())break;
        if (arr[i] <= q.front()) {
            ans++;
            q.pop();
        }
    }
    cout << ans << endl;
}

signed main() {
#ifdef ONLINE_JUDGE
#else
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    ios::sync_with_stdio(0);
    cout.tie(0);
    cin.tie(0);
    int t = 1;
//    cin >> t;
    while (t--)
        solve();
    return 0;
}

B. Minimum Ternary String

​ 观察题目可以发现每次移动可以看作只有1在左右移动,0与2不能交换位置,所以最总答案是把所有1聚在一块原本0和2的位置不发生改变,于是问题变为把1插在哪里使得字符串字典序最小,显然插在第一个不是0的位置前即可

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define inf INT32_MAX
#define PII pair<int,int>
#define endl '\n'

inline void solve() {
    string s;
    cin >> s;

    string ans;

    int cnt = 0;
    for (auto c: s) {
        if (c == '1') ++cnt;
        else ans += c;
    }

    int n = ans.size();
    int pos = -1;
    while (pos + 1 < n && ans[pos + 1] == '0') ++pos;
    ans.insert(pos + 1, string(cnt, '1'));

    cout << ans << endl;
}

signed main() {
#ifdef ONLINE_JUDGE
#else
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    ios::sync_with_stdio(0);
    cout.tie(0);
    cin.tie(0);
    int t = 1;
//    cin >> t;
    while (t--)
        solve();
    return 0;
}

C. Annoying Present

​ 由于每一个位置都会加上\(x_i\)所以x与选择的位置没有关系,那么来讨论选择位置与\(d_i\)的关系。稍微计算一下可也得到

​ · 如果\(d_i\le0\)在数组的中点位置可以确保总和最大

​ · 如果\(d_i>0\)那么在数组的左右端点处可以确保总和最大

​ 由此可以得到结果

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define inf INT32_MAX
#define PII pair<int,int>
#define endl '\n'

inline void solve() {
    int n, q;
    cin >> n >> q;
    int sum = 0;
    while (q--) {
        int x, d;
        cin >> x >> d;
        sum += x * n;
        if (d < 0) {
            int cnt = (n - 1) / 2;
            sum += (cnt * d + d) * cnt + (n % 2 == 0) * (cnt * d + d);
        } else {
            int cnt = n - 1;
            sum += (d + cnt * d) * cnt / 2;
        }
    }
    double ans = 1.0 * sum / n;
    cout << fixed << setprecision(12) << ans << endl;
}


signed main() {
#ifdef ONLINE_JUDGE
#else
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    ios::sync_with_stdio(0);
    cout.tie(0);
    cin.tie(0);
    int t = 1;
//    cin >> t;
    while (t--)
        solve();
    return 0;
}

D. Relatively Prime Graph

​ 首先观察数据发现\(n,m\leq10^5\)由于边的上限是\(10^5\)那么简单打表一下可以发现当\(n=600时\),可以连的边就已经大于\(10^5\)了,而暴力枚举只需要\(n^2\)的复杂度那么,如果对于所有数直接暴力枚举即可

​ 需要特判,如果边小于点的个数减一即\(m<n-1\)那么无法构造出一个n点m边的图输出\(Impossible\)

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define inf INT32_MAX
#define PII pair<int,int>
#define endl '\n'

inline void solve() {
    int n, m;
    cin >> n >> m;
    vector<PII > ans;
    if (m < n - 1) {
        puts("Impossible");
        return;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            if (__gcd(i, j) == 1)
                ans.push_back(make_pair(i, j));
            if (ans.size() == m)break;
        }
        if (ans.size() == m)break;
    }
    if (ans.size() == m) {
        cout << "Possible\n";
        for (auto [x, y]: ans)cout << x << ' ' << y << endl;
    } else cout << "Impossible\n";
}


signed main() {
#ifdef ONLINE_JUDGE
#else
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    ios::sync_with_stdio(0);
    cout.tie(0);
    cin.tie(0);
    int t = 1;
//    cin >> t;
    while (t--)
        solve();
    return 0;
}

E. Intercity Travelling

​ 题目中实际上每次是从1号点开始移动

​ 乘以\(2^{n-1}\)相当于把原本计算的期望的分母消了,所以计算所有可能得难度和即可

​ 设\(pos\)假设在\(pos\)点位置产生了\(a_i\)的难度贡献,那么说明在\((pos-i,pos)\)这个区间内不存在休息点而其余位置不管有没有休息点都可以那么对于每一个满足条件的\(pos,pos\in[i,n]\)都会产生\(a_i\)的难度贡献,注意

​ ·当\(i = pos\)的时候区间左端点被固定,产生的贡献为\(a_i\cdot 2^{n-i}\)

​ ·当\(i\neq pos\)的时候区间左右都没有被固定,但是需要排除\(i=pos\)的情况,所以产生的贡献就是\(a_i\cdot (n - i)\cdot 2^{n-i-1}\)

​ 综上答案通式为\(ans=\sum_{i=1}^n(2^{n-i}+(n-i)\cdot2^{n-i-1})\cdot a_i\)

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define inf INT32_MAX
#define PII pair<int,int>
#define endl '\n'
const int mod = 998244353;

inline void solve() {
    int n;
    cin >> n;
    int ans = 0;
    vector<int> num(n + 1);
    num.front() = 1;
    for (int i = 1; i <= n; i++)num[i] = (num[i - 1] * 2) % mod;
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        ans = (ans + (num[n - i] + (n - i) * num[n - i - 1]) % mod * x % mod) % mod;
    }
    cout << ans << endl;
}


signed main() {
#ifdef ONLINE_JUDGE
#else
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    ios::sync_with_stdio(0);
    cout.tie(0);
    cin.tie(0);
    int t = 1;
//    cin >> t;
    while (t--)
        solve();
    return 0;
}

F. Dominant Indices

​ 题目大意为:给定一棵以1为根节点的有n个节点的树,设\(f(x,i)为x子树中到x距离为i的节点数\),对于每个点求一个最小的\(k,使得f(x,k)最大。(1\leq n\leq 10^6)\)

​ 暴力思想:用\(f[x][i]\)表示在x子树内与x距离为i的节点数,暴力枚举\(\sum_{y\in son[x]}f[y][i-1]\)\(n^2\)的复杂度会\(TLE\)

​ 用长链剖分进行优化

​ 1.先搜索重儿子,搜完一条链上的重儿子,回溯时,先让父节点继承重儿子答案,

​ 2.再搜索轻儿子,回溯时,暴力合并轻儿子上的信息,然后更新答案

​ 如果\(f数组\)开成\(N^2\)空间会炸掉。可以巧妙的通过指针给每个节点动态分配内存才来解决这个问题

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define inf INT32_MAX
#define PII pair<int,int>
#define endl '\n'
const int mod = 998244353;
const int N = 1e6 + 7;
vector<int> e[N];
int son[N], len[N];
int buf[N], *f[N], *p = buf, ans[N];
//buf相当于f数组的第二维
//*f则是f数组的第一维
//f[i][j]第一维表示节点信息,第二维表示距离
void dfs(int x, int fa) {
    for (int y: e[x]) {
        if (y == fa)continue;
        dfs(y, x);
        if (len[son[x]] < len[y])son[x] = y;//dfs跑长儿子
    }
    len[x] = len[son[x]] + 1;
}

void dp(int x, int fa) {
    f[x][0] = 1;//到本身距离为0的点是本身
    if (son[x]) {//优先跑长链,后续答案向长链上合并
        f[son[x]] = f[x] + 1;//共享内存,拷贝数组
        dp(son[x], x);
        ans[x] = ans[son[x]] + 1;//继承长儿子节点答案
    }
    for (int y: e[x]) {
        if (y == fa || y == son[x])continue;
        f[y] = p,p += len[y];//给y开头的链申请空间
        dp(y, x);
        for (int j = 1; j <= len[y]; j++) {
            f[x][j] += f[y][j - 1];//累加所有子节点状态
            if (f[x][j] > f[x][ans[x]] || (f[x][j] == f[x][ans[x]] && j < ans[x]))//第一种为节点数更多,第二种则为k值更小
                ans[x] = j;
        }
    }
    if (f[x][ans[x]] == 1)ans[x] = 0;//唯一节点为本身
}

inline void solve() {
    int n;
    cin >> n;
    for (int i = 1; i < n; i++) {
        int x, y;
        cin >> x >> y;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dfs(1, 0);
    f[1] = p,p += len[1];//为根节点申请空间
    dp(1, 0);
    for (int i = 1; i <= n; i++)cout << ans[i] << "\n";
}


signed main() {
#ifdef ONLINE_JUDGE
#else
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    ios::sync_with_stdio(0);
    cout.tie(0);
    cin.tie(0);
    int t = 1;
//    cin >> t;
    while (t--)
        solve();
    return 0;
}

如有不懂请看董晓老师的b站视频讲解 D34【模板】长链剖分 CF1009F Dominant Indices

posted @ 2024-12-18 19:11  让你飞起来  阅读(23)  评论(0)    收藏  举报