Educational Codeforces Round 125 (Rated for Div. 2) C ~ E

Educational Codeforces Round 125 (Rated for Div. 2)

C. Bracket Sequence Deletion

prob. : 给一个括号序列,定义一次操作为将最短的满足括号匹配的前缀或者回文前缀删去,问操作次数和剩余的字母长度

idea:

删最短的,则考虑开头两位字母

()(()) 这些情况可以直接删去,只有)( 会和后面匹配同时目标是形成回文串,仔细观察发现这个回文串是 ) ( ( ( … ( ) )(((\dots () )(((() 的形式,则其实直接找下一个) 就可以

btw,我不知道我一上来不动脑子的时候套马拉车为什么还能给我写炸来(你

code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const ll inf = 0x3f3f3f3f;
const ll N = 1e6 + 10;

int n;
char s[N];

signed main() {

    ll T;
    cin >> T;
    while (T--) {
        scanf("%d %s", &n, s + 1);
        int id = 1;
        int ans = 0;
        while(id <= n) {
            if(id + 1 <= n && (!(s[id] == ')' && s[id +1] == '('))) {
                id += 2;
                ans++;
                continue;
            }
            bool flag = 0;
            for(int i = id +2; i <=n; ++ i) {
                if(s[i] ==')') {
                    id = i + 1;
                    ans++;
                    flag = 1;
                    break;
                }
            }
            if(!flag) {
                break;
            }
        }

        cout<<ans << " "<< n + 1 - id << endl;

    }
    return 0;
}

D. For Gamers. By Gamers.

prob. :玩游戏,有n种单位,每个单位有一个单位价格 c i c_i ci,单位能造成的伤害 d i d_i di,单位血量 h i h_i hi,q次询问,每次一个boss,伤害D,血量H,对于每个boss,只能用不超C的钱购买同一种单位,同时要求杀死boss且没有单位被boss杀死的最少花费,伤害为持续伤害,或输出不可能

idea : H s u m d i < h i D \frac{H}{sumd_i} < \frac{h_i}{D} sumdiH<Dhi

⇒ s u m d i × h i > H × D \Rightarrow sumd_i \times h_i > H \times D sumdi×hi>H×D

要求花费最小,对于从0到C的每个价格c,考虑该花费能造成的最大收益(最大hd)

枚举每个单位的数量更新c
最后是将c变成“花费不大于c的最大价值”
然后二分位置

code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const ll inf = 0x3f3f3f3f;
const ll N = 1e6 + 10;

ll c[N], d[N], h[N];
ll damage[N];

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

    ll n, C;
    cin >> n >> C;

    for (ll i = 1; i <= n; ++i) {
        cin >> c[i] >> d[i] >> h[i];
        damage[c[i]] = max(damage[c[i]], d[i] * h[i]);
    }
    for (ll i = 1; i <= C; ++i) {
        for (ll k = 2; k * i <= C; ++k) {
            damage[k * i] = max(damage[k * i], damage[i] * k);
        }
    }

    for (ll i = 1; i <= C; ++i) {
        damage[i] = max(damage[i], damage[i - 1]);
    }
    ll q;
    cin >> q;
    while (q--) {
        ll D, H;
        cin >> D >> H;
        ll num =  D*H;
        ll l = 1, r = C, ans = -1;
        while(l <= r) {
            ll mid = (l +r) >> 1;
            if(damage[mid] > num) {
                ans = mid;
                r = mid -1;
            }
            else l = mid + 1;
        }
        cout<<ans << " ";
    }


    return 0;
}

E. Star MST

prob. :给一个n个点无向完全图,每条边有个边权,要求构造每条边边权在w范围内,与节点1相连的所有边构成该图最小生成树的图的个数

idea :

dls题解发好快,dlsyyds

对于每条与1不直接相连的边, 有 e x y ≥ e 1 x , e x y ≥ e 1 y e_{xy} \ge e_{1x} , e_{xy} \ge e_{1y} exye1x,exye1y

考虑构造,对于与1直接相连的边,边权按大小依次构造,

(到这里倒是想到了,后面不会了,我为什么不能"不会了就想想能不能套dp"捏

def d p [ i ] [ j ] dp[i][j] dp[i][j] 为所有边权小于等于i的边,已经和1相连的边为j个

转移考虑边权为i+1 的边,有k个点和1连边的边权是i+1,

选点: ( k n − 1 − j ) (^{n-1-j}_{k}) (kn1j);这些边权定了之后,这k个点内部的边边权限制已经定完了(即 ≥ i + 1 \ge i + 1 i+1) , 这k个点和前面j个点的连边的限制也是 ≥ i + 1 \ge i + 1 i+1,这些边的个数总共是 k × ( k − 1 ) 2 + j × k \frac{k \times(k - 1)}{2} + j \times k 2k×(k1)+j×k

对于每个固定的k对于答案的贡献是 ( k n − 1 − j ) × ( w − i ) k × ( k − 1 ) 2 + j × k (^{n-1-j}_{k}) \times (w - i)^{\frac{k \times(k - 1)}{2} + j \times k} (kn1j)×(wi)2k×(k1)+j×k

枚举k转移,总体复杂度 O ( n 3 ) O(n^3) O(n3)

code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const ll mod = 998244353;
const ll inf = 0x3f3f3f3f;
const ll N = 1e4 + 10;

ll fac[N];
ll invfac[N];
ll invn[N];

void init() {
    fac[0] = fac[1] = invfac[0] = invfac[1] = invn[0] = invn[1] = 1;
    for (ll i = 2; i < N; ++i) {
        fac[i] = fac[i - 1] * i % mod;
        invn[i] = (mod - mod / i) * invn[mod % i] % mod;
        invfac[i] = invfac[i - 1] * invn[i] % mod;
    }
}

ll C(ll up, ll down) {
    if (up > down) return 0;
    if (up < 0 || down < 0) return 0;
    ll res = fac[down];
    res = res * invfac[down - up] % mod;
    res = res * invfac[up] % mod;
    return res;
}

ll qpow(ll x, ll n) {
    ll res = 1;
    while (n) {
        if (n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

ll dp[300][300];


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

    init();

    ll n, w;
    cin >> n >> w;

    dp[0][0] = 1;
    for (ll i = 1; i <= w; ++i) {
        for (ll j = 0; j < n; ++j) {
            for (ll k = 0; k + j < n; ++k) {
                ll num = dp[i - 1][j] * C(k, n - 1 - j) % mod * qpow(w - i + 1, k * (k - 1) / 2 + j * k) % mod;
                dp[i][k + j] = (dp[i][k + j] + num) % mod;
            }
        }
    }

    cout << dp[w][n - 1] << endl;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值