Educational Codeforces Round 161(A-F)

Educational Codeforces Round 161

A. Tricky Template

void solve(int cs) {
    int n;
    cin >> n;
    string a, b, c;
    cin >> a >> b >> c;
    for (int i = 0; i < n; i++) {
        if (a[i] != c[i] && b[i] != c[i]) {
            cout << "YES\n";
            return;
        }
    }
    cout << "NO\n";
}   

B. Forming Triangles

根据边长特性只能选择两个相同的大边和一条小于等于他的边构成等腰/等边三角形,从大到小排好序后求组合数即可

bool cmp(int a, int b) {
    return a > b;
}

void solve(int cs) {
    int n;
    cin >> n;
    VI a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    int ans = 0, cnt = 1;
    sort(a.begin(), a.end(), cmp);
    for (int i = 1; i < n; i++) {
        cnt = 1;
        while (a[i] == a[i - 1] && i < n) {
            cnt++;
            i++;
        }
        if (cnt == 1) continue;
        if (cnt == 2) {
            ans += ((cnt * (cnt - 1) / 2) * (n - i));//(comb(cnt, 2) * (n - i))------((cnt * (cnt - 1) / 2) * (n - i))
            continue;
        }
        ans += ((cnt * (cnt - 1) * (cnt - 2) / 6) + ((cnt * (cnt - 1) / 2) * (n - i)));//(comb(cnt, 3) + comb(cnt, 2) * (n - i))------((cnt * (cnt - 1) * (cnt - 2) / 6) + ((cnt * (cnt - 1) / 2) * (n - i)))
    }
    cout << ans << endl;
}   

C. Closest Cities

记一下前缀和后缀和即可

struct Node {
    int x, l, r;
};

void solve(int cs) {
    int n;
    cin >> n;
    vector<Node> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i].x;
    a[1].r = 1;a[n].l = 1;
    for (int i = 2; i < n; i++) {
        if (a[i].x - a[i - 1].x > a[i + 1].x - a[i].x) a[i].r = 1;
        else a[i].l = 1;
    }
    VI sum1(n + 1);
    VI sum2(n + 1);
    for (int i = 2; i <= n; i++) {
        sum1[i] = sum1[i - 1] + (a[i - 1].r == 1 ? 1 : a[i].x - a[i - 1].x);
    }
    for (int i = n - 1; i > 0; i--) {
        sum2[i] = sum2[i + 1] + (a[i + 1].l == 1 ? 1 : a[i + 1].x - a[i].x);
    }
    int m;
    cin >> m;
    while (m--) {
        int x, y;
        cin >> x >> y;
        if (x < y) {
            cout << sum1[y] - sum1[x] << endl;
        }
        else {
            cout << sum2[y] - sum2[x] << endl;
        }
    }
}   

D. Berserk Monsters

按照题意用链表储存怪的相对位置关系,用set储存每一轮每一只怪防御力与收到伤害的差,然后按题意模拟即可

void solve(int cs) {
    int n;
    cin >> n;
    VI a(n + 1);
    VI d(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> d[i];
    VI r(n + 1);
    VI l(n + 1);
    for (int i = 1; i <= n; i++) {
        r[i] = i + 1;
        l[i] = i - 1;
    }
    r[n] = 0;
    set<pii > s;
    for (int i = 1; i <= n; i++) s.insert(make_pair(d[i] - a[l[i]] - a[r[i]], i));

    VI v(n + 1);
    VI ans(n + 1);
    for (int i = 1; i <= n; i++) {
        VI ddd;
        while(!s.empty() && s.begin()->first < 0) {
            ddd.pb(s.begin()->second);
            v[s.begin()->second] = 1;
            s.erase(s.begin());
        }
        if (ddd.empty()) break;
        ans[i] = ddd.size();
        for (auto x : ddd) {
            if (l[x] && !v[l[x]]) s.erase(make_pair(d[l[x]] - a[l[l[x]]] - a[r[l[x]]], l[x]));
            if (r[x] && !v[r[x]]) s.erase(make_pair(d[r[x]] - a[l[r[x]]] - a[r[r[x]]], r[x]));
            r[l[x]] = r[x];
            l[r[x]] = l[x];
            if (l[x] && !v[l[x]]) s.insert(make_pair(d[l[x]] - a[l[l[x]]] - a[r[l[x]]], l[x]));
            if (r[x] && !v[r[x]]) s.insert(make_pair(d[r[x]] - a[l[r[x]]] - a[r[r[x]]], r[x]));
        }
    }
    for (int i = 1; i <= n; i++) cout << ans[i] << " ";
    cout << endl;
}   

E - Increasing Subsequences

x x x个数所组成的递增序列有 2 x 2^x 2x个递增子序列,所以将所给数二进制拆分,首先构造出最高位,然后按照二进制位上数字进行构造

void solve(int cs) {
    int x;
    cin >> x;
    stack<int> st;
    while(x) {
        st.push(x & 1);
        x >>= 1;
    }
    int cnt = st.size();
    VI ans;
    for (int i = 1; i < cnt; i++) {
        ans.pb(i);
    }
    st.pop();cnt--;
    while(!st.empty()) {
        if (st.top()) {
            ans.pb(cnt);
        }
        st.pop();
        cnt--;
    }
    cout << ans.size() << endl;
    for (auto i : ans) {
        cout << i << " ";
    }
    cout << endl;
}   

F - Replace on Segment

d p 1 [ l ] [ r ] [ k ] dp1[l][r][k] dp1[l][r][k]表示把区间 [ l ,   r ] [l,\ r] [l, r]中所有数变成 k k k的最小操作数,最后答案显然是 min ⁡ i ∈ [ 1 , x ] { d p 1 [ 1 ] [ n ] [ i ] } \min\limits_{i\in[1,x]} \{dp1[1][n][i]\} i[1,x]min{dp1[1][n][i]}

接下来考虑如何进行状态转移,将一个区间全部变成相同的数 k k k有两种方法:

  • 直接将区间内所有非 k k k数字全部变成 k k k
  • 将区间内所有 k k k全变成非 k k k,然后再通过一次操作将整个区间都变成 k k k,下面的代码中 d p 2 [ l ] [ r ] [ k ] dp2[l][r][k] dp2[l][r][k]表示的就是将区间 [ l ,   r ] [l,\ r] [l, r]中的所有数变成非 k k k的最小操作数

弄清楚这些之后进行区间dp即可

int dp1[N][N][N], dp2[N][N][N], a[N];

void solve(int cs) {
    int n, x;
    cin >> n >> x;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        for (int j = 1; j <= x; j++) {
            dp1[i][i][j] = 1;
            dp2[i][i][j] = 0;
        }
        dp1[i][i][a[i]] = 0;
        dp2[i][i][a[i]] = 1;
    }
    for (int len = 2; len <= n; len++) {
        for (int l = 1; l + len - 1 <= n; l++) {
            int r = l + len - 1;
            for (int k = 1; k <= x; k++) {
                dp1[l][r][k] = llinf;
                dp2[l][r][k] = llinf;
                for (int t = l; t < r; t++) {
                    dp1[l][r][k] = min(dp1[l][r][k], dp1[l][t][k] + dp1[t + 1][r][k]);
					dp2[l][r][k] = min(dp2[l][r][k], dp2[l][t][k] + dp2[t + 1][r][k]);
                }
            }
            VI xl(x + 2);xl[0] = llinf;
            VI xr(x + 2);xr[x + 1] = llinf;
            for (int k = 1; k <= x; k++) xl[k] = min(dp1[l][r][k], xl[k - 1]);
            for (int k = x; k > 0; k--) xr[k] = min(dp1[l][r][k], xr[k + 1]);
            for (int k = 1; k <= x; k++){
				dp2[l][r][k] = min(dp2[l][r][k], min(xl[k - 1], xr[k + 1]));
                dp1[l][r][k] = min(dp1[l][r][k], dp2[l][r][k] + 1);
			}
        }
    }

    int ans = llinf;
    for (int i = 1; i <= x; i++) {
        ans = min(ans, dp1[1][n][i]);
    }
    cout << ans << endl;
}   
  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值