Educational Codeforces Round 117

本文最后更新于:2 年前

D. For Gamers. By Gamers.

题意:玩家有 $C$ 块钱,可以从酒馆中招募随从,每个随从有攻击力 $d_i$,生命 $h_i$,花费 $c_i$ 块钱。接下来有若干轮战斗,每轮战斗有一个敌人,攻击力 $D_j$,生命 $H_j$。每轮相互独立(钱也独立),玩家可以招募若干个相同随从来和怪物战斗,但要求己方没有随从会死亡。问每一轮战斗需要花费至少多少金币

战斗不是回合制的,双方连续攻击。

解:因为是双方连续攻击,所以两个怪物战斗,我放胜利的条件即 $H/d\lt h/D$,换算一下即:$H\times D\lt h\times d$

那么每个怪物的战斗力都可以直接用其攻击乘上生命来衡量。

如果购买多个随从同时作战,因为要求不能死任何一个,所以相当于攻击力可叠加,但生命值不能叠加。其实战斗力是完全等比例增长的。

那么就变成了一个背包问题,但数据范围显然不允许 $n^2$,这时发现题目又有一个条件:只能购买相同随从。

那么在背包转移时,只需要枚举每个随从花费金币的倍数去更新即可。复杂度是经典调和级数 $O(nlogn)$

最后每轮询问二分找一下大于这个怪物战斗力的最小花费(

ll n, m;
ll mxc[maxn], f[maxn];
 
void solve(){
    ll C;
    cin >> n >> C;
    for(ll i=1;i<=n;i++){
        ll c, x, y;
        cin >> c >> x >> y;
        f[c] = max(f[c], x * y);
    }
    for(ll i=1;i<=C;i++){
        f[i] = max(f[i], f[i-1]);
        for(ll j=2;j*i<=C;j++){
            f[j*i] = max(f[j*i], f[i] * j);
        }
    }
 
    cin >> m;
    for(ll i=1;i<=m;i++){
        ll x, y;
        cin >> x >> y;
        if(f[C] <= x * y){
            cout << "-1" << " \n"[i == m];
            continue;
        }
        ll p = lower_bound(f+1, f+C+1, x * y + 1) - f;
        cout << p << " \n"[i == m];
    }
}

E. Star MST

题意:对于 $n$ 个节点的完全图,每条边不超过 $m$,如果所有和1相连的边能构成最小生成树,那么这个完全图是好的。请问一共有多少种好的完全图?

从kruskal角度考虑:在我们依次将边插入时,除了1以外不能存在其他联通块,否则即不是最小生成树。

设 $dp[i][j]$ 为已经将小于等于 $j$ 的所有边插入,此时1的联通块大小为 $i$ 的答案。

转移时,插入所有长度为 $j+1$ 的边,枚举有多少个点因此并入联通块了(假设有 $k$ 个),那么一共有 $C_{n-i}^{k}$ 种取法,并入后新增的边数为 $k\times(k-1)/2+i\times k$,其中 $k$ 条确定长度为 $j+1$ 了,另外的边长度可以在 $[j+1,m]$ 之间任选。

转移:$dp[i+k][j+1]+=dp[i][k]\times C_{n-i}^k\times(m-j)^{k\times (k-1)/2+(i-1)\times k}$

ll n, m;
ll f[300][300];
 
ll qpow(ll x, ll p){
    ll r = 1;
    while(p > 0){
        if(p & 1) r = r * x % mode;
        x = x * x % mode;
        p /= 2;
    }
    return r;
}
 
ll inv(ll x){ return qpow(x, mode-2);}
ll jc[maxn];
ll C(ll n, ll m){
    return jc[n] * inv(jc[m] * jc[n-m] % mode) % mode;
}
 
void solve(){
    jc[0] = 1;
    for(int i=1;i<=1000;i++) jc[i] = jc[i-1] * i % mode;
 
    cin >> n >> m;
    f[1][0] = 1;
    for(int i=1;i<=n;i++){
        for(int j=0;j<m;j++){
            for(int k=0;i+k<=n;k++){
                (f[i+k][j+1] += f[i][j] * C(n-i, k) % mode * qpow(m-j, k*(k-1)/2 + (i-1)*k) % mode) %= mode;
            }
        }
    }
    cout << f[n][m] << '\n';
}

F. Words on Tree

写的稀碎,看看思路就好,代码别看了(

题意:现在有一棵树,每个点上有一个字符,给定了若干个限制条件,形如 $x,y,s$,表示树上 $x$ 到 $y$ 的路径上的字符连起来必须是 $s$ 或者 $s$ 的反串。你需要填写树上的字符使得所有限制条件都被满足。

既然有限制条件,并且这个限制条件显然是二选一,我们肯定要往2-SAT方向去想

对每个限制条件建两个点,一个表示字符正着放,另一个表示反着放。问题是怎么连边,似乎怎么连都得 $n^2$ 条边

这里的关键在于:并不需要把所有约束条件都表示出来,只要一个约束条件已经被已有约束包含,就可以忽略它

具体而言,我们在每一个节点处随便选取一个涉及这个节点的约束条件,那么可以确定这个节点一定是两种字符之一,我们称这个被选择的约束条件为哨兵。

对于任意两个有重复部分的约束条件,它们都去和哨兵建立联系,它们两个之间的约束关系也就通过哨兵建立了。

int n, m;
vector<int> vp[maxn], vp2[maxn];
int dep[maxn], fa[maxn], bin[maxn];
char chr[maxn][2];
 
void dfs(int p, int f){
    fa[p] = f;
    dep[p] = dep[f] + 1;
    for(int v: vp[p]){
        if(v != f) dfs(v, p);
    }
}
 
vector<int> getPath(int u, int v){
    vector<int> r1, r2;
    while(u != v){
        if(dep[u] > dep[v]) r1.push_back(u), u = fa[u];
        else r2.push_back(v), v = fa[v];
    }
    r1.push_back(u);
    for(int i=(int)r2.size()-1;i>=0;i--) r1.push_back(r2[i]);
    return r1;
}
 
int dfn[maxn], low[maxn], bel[maxn], dfcnt = 0;
stack<int> stk;
int belid = 0;
 
void tarjian(int p){
    dfn[p] = low[p] = ++dfcnt;
    stk.push(p);
    for(int v: vp2[p]){
        if(!dfn[v]) tarjian(v);
        if(!bel[v]) low[p] = min(low[p], low[v]);
    }
    if(dfn[p] == low[p]){
        bel[p] = ++belid;
        while(stk.top() != p){
            bel[stk.top()] = belid;
            stk.pop();
        }
        stk.pop();
    }
}
 
void solve(){
    cin >> n >> m;
    for(int i=1;i<n;i++){
        int x, y;
        cin >> x >> y;
        vp[x].push_back(y);
        vp[y].push_back(x);
    }
    dfs(1, 0);
    struct query{
        int x, y;
        string s;
    };
    vector<query> qr(m + 1);
    for(int i=1;i<=m;i++){
        int x, y;
        string s;
        cin >> x >> y >> s;
        qr[i] = {x, y, s};
        auto l = getPath(x, y);
        for(int j=0;j<l.size();j++){
            if(!bin[l[j]]){
                chr[l[j]][0] = s[j];
                chr[l[j]][1] = s[s.size()-1-j];
                bin[l[j]] = i;
            }
        }
    }
    for(int i=1;i<=m;i++){
        string s = qr[i].s;
        auto l = getPath(qr[i].x, qr[i].y);
        for(int j=0;j<l.size();j++){
            char mc[2] = {s[j], s[s.size()-1-j]};
            char *yc = chr[l[j]];
            int yj = bin[l[j]];
            if(yj == i) continue;
            if(yc[0] == yc[1] && mc[0] == mc[1] && yc[0] == mc[0]) continue;
            if(yc[0] == yc[1]){
                if(mc[0] != yc[0] && mc[1] != yc[0]){
                    cout << "NO\n";
                    return;
                }else{
                    if(mc[0] == yc[0]) vp2[m+i].push_back(i);
                    else vp2[i].push_back(m+i);
                }
            }else if(mc[0] == mc[1]){
                if(mc[0] != yc[0] && mc[0] != yc[1]){
                    cout << "NO\n";
                    return;
                }else{
                    if(mc[0] == yc[0]) vp2[yj+m].push_back(yj);
                    else vp2[yj].push_back(yj+m);
                }
            }else{
                bool fd = false;
 
                for(int u=0;u<2;u++){
                    if(mc[u] != yc[0] && mc[u] != yc[1]){
                        vp2[i+m*u].push_back(i+m*(u^1));
                    }
                }
                for(int u=0;u<2;u++){
                    if(yc[u] != mc[0] && yc[u] != mc[1]){
                        vp2[yj+m*u].push_back(yj+m*(u^1));
                    }
                }
 
                for(int u=0;u<2;u++){
                    for(int v=0;v<2;v++){
                        if(mc[u] == yc[v]){
                            fd = true;
                            vp2[i+m*u].push_back(yj+m*v);
                            vp2[yj+m*v].push_back(i+m*u);
                        }
                    }
                }
                if(!fd){
                    cout << "NO\n";
                    return;
                }
            }
        }
    }
    for(int i=1;i<=2*m;i++) if(!dfn[i]) tarjian(i);
    for(int i=1;i<=m;i++){
        if(bel[i] == bel[i+m]){
            cout << "NO\n";
            return;
        }
    }
    vector<int> cs(m+1);
    for(int i=1;i<=m;i++){
        if(bel[i] < bel[i+m]) cs[i] = 0;
        else cs[i] = 1;
    }
    cout << "YES\n";
    for(int i=1;i<=n;i++){
        if(!bin[i]) cout << 'a';
        else cout << chr[i][cs[bin[i]]];
    }
    cout << '\n';
}

Educational Codeforces Round 117
http://www.lxtyin.ac.cn/2022/03/23/2022-03-23-Educational Codeforces Round 125/
作者
lx_tyin
发布于
2022年3月23日
许可协议