CF 1500-1800训练 A - M 训练记录

题目链接

A - Decrease the Sum of Digits

思路:

记录以下到那个位置会大于 \(s\),然后前一个位置的数加一,之后的全部变为零即可。

注意特判到某个位置刚好等于 \(s\) 的情况。

代码:
int main(){

    int t; cin >> t;
    while(t --){
        string str; int s;
        cin >> str >> s;
        ll cur = 0;
        int lenStr = str.size();
        for(int i = 0; i < lenStr; i ++) cur = cur * 10 + str[i] - '0';
        int sum = 0, idx = -1;
        for(int i = 0; i < lenStr; i ++){
            sum += str[i] - '0';
            if(sum > s){
                idx = i;
                break;
            } else if(sum == s){
                for(int j = i + 1; j < lenStr; j ++) sum += str[j] - '0';
                if(sum > s){
                    idx = i;
                }
                break;
            }
        }
        if(idx == -1) puts("0");
        else{
            //cout << "idx = " << idx << endl;
            if(idx == 0){
                str = '1' + str;
                idx ++;
            } else{
                str[idx - 1] ++;
            }
            lenStr = str.size();
            for(int i = idx; i < str.size(); i ++) str[i] = '0';
            ll now = 0;
            //cout << str << endl;
            for(int i = 0; i < lenStr; i ++) now = now * 10 + str[i] - '0';
            cout << now - cur << endl;
        }
    }

    
    return 0;
}

B - Two Platforms

思路:

显然 \(y\) 坐标是没有用的。

\(x\) 坐标排序,预处理出 \(cnt[i]\) 数组,含义是 \(i\)\(n\) 之间放一个平台可以覆盖最多的点的数量。

然后遍历一遍即可。

代码:
const int N = 2e5 + 10;

int x[N], cnt[N];

int main(){

   int t; cin >> t;
   while(t --){
       int n, k; cin >> n >> k;
       memset(cnt, 0, sizeof cnt);
       for(int i = 1; i <= n; i ++) scanf("%d", &x[i]);
       for(int i = 1, y; i <= n; i ++) scanf("%d", &y);
       sort(x + 1, x + n + 1);
       for(int i = n; i >= 1; i --){
           int mx = x[i] + k;
           int idx = upper_bound(x + 1, x + n + 1, mx) - x;
           cnt[i] = max(cnt[i + 1], idx - i);
       }
       int ans = 0;
       for(int i = 1; i <= n; i ++){
           int mx = x[i] + k;
           int idx = upper_bound(x + 1, x + n + 1, mx) - x;
           ans = max(ans, (idx - i) + cnt[idx]);
       }
       cout << ans << endl;
   }

   
   return 0;
}

C - Maximum Distributed Tree

思路:

把每条边的贡献算出来,然后贪心的赋值。

代码:
const int N = 1e5 + 10;
const int mod = 1e9 + 7;

vector<int> g[N];

ll cnt[N];
ll p[N], q[N];

void dfs(int cur, int fa){
   cnt[cur] = 1;
   for(auto it : g[cur]){
       if(it == fa) continue;
       dfs(it, cur);
       cnt[cur] += cnt[it];
   }
}


int main(){

   int t; cin >> t;
   while(t --){
       int n, m; cin >> n;
       for(int i = 1; i <= n; i ++){
           g[i].clear();
           cnt[i] = 0;
           p[i] = 1;
       }
       for(int i = 1, u, v; i < n; i ++){
           scanf("%d %d", &u, &v);
           g[u].pb(v); g[v].pb(u);
       }
       cin >> m;
       for(int i = 1; i <= m; i ++) scanf("%lld", p + i);
       dfs(1, -1);
       m = max(m, n - 1);
       sort(p + 1, p + m + 1);
       for(int i = n; i <= m; i ++) p[n - 1] = p[n - 1] * p[i] % mod;
       for(int i = 1; i < n; i ++) q[i] = cnt[i + 1] * (n - cnt[i + 1]);
       sort(q + 1, q + n);
       ll ans = 0;
       for(int i = 1; i < n; i ++) 
           ans = (ans + (q[i] % mod * p[i] % mod) % mod) % mod;
       cout << ans << endl;
   }
   return 0;
}

D - RPG Protagonist

思路:

由于 \(cnts\) 不是很大,所以直接枚举某一个人选择 \(sword\) 的数量即可。

代码:
int main(){

    int t; input(t);
    while(t --){
        ll p, f, cnts, cntw, s, w;
        input(p); input(f);
        input(cnts); input(cntw);
        input(s); input(w);
        ll ans = 0;
        for(int i = 0; i <= cnts; i ++){
            ll tmp = i;
            ll rp = p - i * s;
            if(rp < 0) break;
            ll rcnts = cnts - i;
            ll getw = min(cntw, rp / w);
            ll rcntw = cntw - getw;
            tmp += getw;
            ll t1 = 0, t2 = 0;
            if(w < s){
                ll getw = min(rcntw, f / w);
                t1 = getw;
                ll rf = f - getw * w;
                t1 += min(rcnts, rf / s);
            } else{
                ll gets = min(rcnts, f / s);
                t2 = gets;
                ll rf = f - gets * s;
                t2 += min(rcntw, rf / w);
            }
            tmp += max(t1, t2);
            ans = max(ans, tmp);
        }
        cout << ans << endl;
        
    }

    
    return 0;
}

E - Binary String To Subsequences

思路:

分别记录出 \(0, 1\) 出现的位置,然后遍历一遍,对于当前出现的 \(s[i]\),直接二分去找下一个应该出现的字符(\(0\)\(1\)\(1\)\(0\)),对于找到的字符,可以从记录的容器中删去。

代码:
const int N = 2e5 + 10;

char s[N];
int ans[N];

vector<int> zero, one;

int main(){

    int t; input(t);
    while(t --){
        
        int n; input(n);
        input(s + 1);
        for(int i = 1; i <= n; i ++){
            if(s[i] == '0') zero.pb(i);
            else if(s[i] == '1') one.pb(i);
        }
        
        memset(ans, 0, sizeof ans);
        int k = 0;
        
        for(int i = 1; i <= n; i ++){
            if(ans[i] == 0) ans[i] = ++ k;
            if(s[i] == '1'){
                int idx = upper_bound(all(zero), i) - zero.begin();
                if(idx >= zero.size()) continue;
                if(zero[idx] > i && zero[idx] <= n) {
                    ans[zero[idx]] = ans[i];
                    zero.erase(zero.begin() + idx);
                }
            } else{
                int idx = upper_bound(all(one), i) - one.begin();
                if(idx >= one.size()) continue;
                if(one[idx] > i && one[idx] <= n) {
                    ans[one[idx]] = ans[i];
                    one.erase(one.begin() + idx);
                }
            }
        }

        output(k);
        for(int i = 1; i <= n; i ++) printf("%d ", ans[i]);
        puts("");
        zero.clear();
        one.clear();
    }


    return 0;
}

F - Colored Rectangles

思路:

直接贪心的错误样例:

r: 3 3
g: 4
b: 5

wa = 20
ac = 27

\(dp\) 暴力则可以完整的枚举出所有的情况。

\(f[i][j][k]\) :红色用来 \(i\) 个,绿色用了 \(j\) 个,蓝色用了 \(k\) 个的最大值。

转移方程:

\[f[i+1][j+1][k] = max(f[i+1][j+1][k], f[i][j][k] + r[i]\times g[j]) \]

\[f[i+1][j][k+1] = max(f[i+1][j][k+1], f[i][j][k] + r[i]\times b[k]) \]

\[f[i][j+1][k+1] = max(f[i][j+1][k+1], f[i][j][k] + g[j]\times b[k]) \]

代码:
const int N = 210;

int R[N], G[N], B[N];
ll f[N][N][N];

int main(){

    int r, g, b;
    input(r); input(g); input(b);
    for(int i = 1; i <= r; i ++) input(R[i]);
    for(int i = 1; i <= g; i ++) input(G[i]);
    for(int i = 1; i <= b; i ++) input(B[i]);

    auto cmp = [&](int a, int b) -> bool {
        return a > b;
    };

    sort(R + 1, R + r + 1, cmp);
    sort(G + 1, G + g + 1, cmp);
    sort(B + 1, B + b + 1, cmp);

    ll ans = 0;

    for(int i = 1; i <= r + 1; i ++)
        for(int j = 1; j <= g + 1; j ++)
            for(int k = 1; k <= b + 1; k ++){
                f[i + 1][j + 1][k] = max(f[i + 1][j + 1][k], f[i][j][k] + R[i] * G[j]);
                f[i + 1][j][k + 1] = max(f[i + 1][j][k + 1], f[i][j][k] + R[i] * B[k]);
                f[i][j + 1][k + 1] = max(f[i][j + 1][k + 1], f[i][j][k] + B[k] * G[j]);
                ans = max(ans, f[i][j][k]);
            }
    output(ans);
    
    return 0;
}

G - Good Subarrays

思路:

思维 + 前缀和

已知:\(sum[i] - sum[j - 1] = i - j + 1 = i - (j - 1)\)

那么:\(sum[i] - i = sum[j - 1] - (j - 1)\)

显然我们只需要计算出 \(sum[i] - i\) 的个数即可。

注:由于前面的公式涉及到了 \(idx = 0\) 的情况,所以 \(sum[0] - 0\) 也要算一个。

代码:
#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int sum[N];

map<int, int> mp;

int main(){

   int t; scanf("%d", &t);
   while(t --){
       mp.clear();
       int n; scanf("%d", &n);
       sum[0] = 0;
       for(int i = 1; i <= n; i ++){
           int x; scanf("%1d", &x);
           sum[i] = sum[i - 1] + x;
           mp[sum[i] - i] ++;
       }
       long long ans = 0;
       mp[0] ++;
       for(auto it : mp) {
           ans += 1ll * it.second * (it.second - 1) / 2;
       }
       cout << ans << endl;
   }

   return 0;
}

H - Same GCDs

思路:

\(a > b\) 时有:\(gcd(a,b) = gcd(a-b,b)\)

那么当 \(a + x >= m\) 时:\(gcd(a + x) = gcd(a + x - m)\).

\((a + x) \% m\)

\(y = (a + x) \% m\)

可得:\(gcd(y, m) = gcd(a,m) = d\)

\(gcd(y, m) = d\) ,可得: \(gcd(\frac{y}{d}, \frac{m}{d}) = 1\)

\(n = \frac{m}{d}\),显然 \(n\) 是已知得,那么我们求得就是 \([2, n]\) 中与 \(n\) 互质的数的个数,

欧拉函数即可解决。

代码:
ll Euler(ll n){
    ll ans = n;
    for(ll i = 2; i * i <= n; i ++){
        if(n % i == 0) ans -= ans / i;
        while(n % i == 0) n /= i;
    }
    if(n > 1) ans -= ans / n;
    return ans;
}

int main(){

    int t; read(t);
    while(t --){
        ll a, m;
        cin >> a >> m;
        ll d = gcd(a, m);
       // out(toStr(d), d);
        cout << Euler(m / d) << endl;
    }
    return 0;
}

I - Ehab and Prefix MEXs

思路:

把未出现的数用 \(b\) 数组记录一下。

如果 \(a[i] == a[i - 1]\) 则输出 \(b\) 中最小的数

如果 \(a[i] \ne a[i - 1]\) 则输出 \(a[i - 1]\)

代码:
int a[N], b[N], c[N];

int main(){

    memset(c, 0, sizeof c);
    int n; cin >> n;
    for(int i = 1; i <= n; i ++) { cin >> a[i]; c[a[i]] = 1; }
    int cntb = 0;
    for(int i = 0; i <= n; i ++) if(!c[i]) b[ ++ cntb] = i;
    cntb = 0;
    a[0] = a[1];
    for(int i = 1; i <= n; i ++){
        if(a[i] == a[i - 1]) cout << b[ ++ cntb] << " ";
        else cout << a[i - 1] << " ";
    }
    return 0;
}

J - Skyscrapers (easy version)

思路:

显然要符合要求,那么曲线必然是先上升后下降(单增和单减属于特殊情况)。曲线即是: ^。

那么我们直接暴力枚举最高点即可。

时间复杂度:\(O(n^2)\)

代码:
    int n; cin >> n;
    vector<int> ans(n), tmp(n);
    ll h = 0;
    for(int i = 0; i < n; i ++) cin >> m[i];
    for(int i = 0; i < n; i ++){
        ll sum = 0;
        tmp[i] = m[i];
        sum += tmp[i];
        for(int j = i + 1; j < n; j ++)  { tmp[j] = min(m[j], tmp[j - 1]); sum += tmp[j]; }
        for(int j = i - 1; j >= 0; j --) { tmp[j] = min(m[j], tmp[j + 1]); sum += tmp[j]; }
        if(sum > h){
            ans = tmp;
            h = sum;
        }
    }
    for(int i = 0; i < n; i ++) cout << ans[i] << " ";
    cout << endl;

K - Yet Another Walking Robot

思路:

用一个 \(map\) 来记录当前移动的状态,如果当前出现的状态在之前就已经出现过了,那么这一段的子串就可以合法删除。

代码:
const int N = 2e5 + 10;

char s[N];

int main(){

    int t; read(t);
    while(t --){
        int n; read(n);
        scanf("%s", s + 1);
        int x = 0, y = 0;
        map<PII, int> mp;
        int d = INF;
        PII ans;
        mp[{0, 0}] = 0;
        for(int i = 1; i <= n; i ++){
            if(s[i] == 'L') x --;
            else if(s[i] == 'R') x ++;
            else if(s[i] == 'U') y --;
            else y ++;
            if(mp.count({x, y}) && d > i - mp[{x, y}]){
                d = i - mp[{x, y}];
                ans = {mp[{x, y}] + 1, i};
            }
            mp[{x, y}] = i;
        }
        if(d == INF) puts("-1");
        else printf("%d %d\n", ans.x, ans.y);
    }
    return 0;
}

L - Fight with Monsters

思路:

记录出每次都是我打死怪兽需要的最少令牌数,然后排个序,即可求出答案。

代码:
ll calc(ll a, ll b, ll x){
    ll t = x / (a + b);
    ll all = t * (a + b);
    if(all == x) t --;
    x -= t * (a + b);
    return x % a == 0 ? x / a - 1 : x / a;
}

int main(){

    ll n, a, b, k;
    cin >> n >> a >> b >> k;
    vector<ll> vec;
    for(int i = 1; i <= n; i ++){
        ll x; read(x);
        vec.pb(calc(a, b, x));
    }
    sort(all(vec));
    int ans = 0;
    for(int i = 0; i < sz(vec); i ++){
        k -= vec[i];
        if(k >= 0) ans ++;
        else break;
    }
    cout << ans << endl;
    return 0;
}

M - String Coloring (easy version)

思路:

当存在三个字符满足以下规律时:\(z\ge y\ge x\)\(x, y, z是变量\)),可以发现此时是无解的。所以我们可以根据这个来判断。

根据以上的结论可以想到原字符串最多可以分为两个不递减子序列。

代码:
const int N = 2e2 + 10;

char s[N];

int main(){

    int n; read(n);
    scanf("%s", s + 1);
    string ans = "";

    char mx1 = 'a', mx2 = 'a';

    for(int i = 1; i <= n; i ++){
        if(s[i] >= mx1) { mx1 = s[i]; ans += '1'; }
        else if(s[i] >= mx2) { mx2 = s[i]; ans += '0'; }
        else {
            puts("NO");
            return 0;
        }
    }
    puts("YES");
    cout << ans << endl;
    return 0;
}

。。。

posted @ 2020-09-23 15:36  nonameless  阅读(278)  评论(0编辑  收藏  举报