cf edu 1700

发布时间 2023-09-10 21:09:12作者: PHarr

1430D. String Deletion

因为要最大话操作次数,所以我们每次删除的时候删除没有被删除最左侧连续相同长度大于等于 2 的部分。

想清楚贪心策略后,用快慢指针就可以\(O(N)\)实现本体。

#include <bits/stdc++.h>

using namespace std;

void solve(){
    int n;
    string s;
    cin >> n >> s;
    if( n == 1 ){
        cout << "1\n";
        return;
    }
    vector<int> a;
    for( int i = 1 , cnt = 1; i < n ; i ++ ){
        if( s[i] == s[i-1] ) cnt ++;
        else if( s[i] != s[i-1] )
            a.push_back(cnt) ,cnt = 1;
        if( i == n-1 )
            a.push_back(cnt);
    }
    n = a.size();
    int pos = 0 , del = 0 , res = 0;
    for( ; pos < n and del < n ; pos ++ ){
        while( del < n and del < pos ) del ++;
        while( del < n and a[del] <= 1 ) del ++;
        if( del < n and a[del] > 1 ) a[del] -- , res ++;
        else break;
    }
    res += ( n - pos + 1 ) / 2;
    cout << res << "\n";
    return ;
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int t;
    cin >> t;
    while( t -- )
        solve();
    return 0;
}

1312D. Count the Arrays

首先从\(m\)个数中选出\(n-1\)\(C_{m}^{n-1}\),然后枚举第\(i\)个数放最大值,然后选择左侧\(i-2\)个数(不包含相同的数)\(C_{n-2}^{i-2}\),再从剩下\(n-i\)个数中选一个作为相同的数\(C_{n-i}^1\),此时剩下的\(n-i-1\)个数就已经确定摆放方式了。

所以答案\(C_m^{n-1}\sum C_{n-2}^{i-2}\times(n-i)\)

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int mod = 998244353;
const int N = 2e5 + 5;
int fact[N];

int power(int x, int y) {
    int ans = 1;
    while (y) {
        if (y & 1) ans = ans * x % mod;
        y >>= 1, x = x * x % mod;
    }
    return ans;
}

int inv(int x) {
    return power(x, mod - 2);
}

int C(int x, int y) {
    return fact[x] * inv(fact[x - y]) % mod * inv(fact[y]) % mod;
}

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    fact[0] = 1;
    for (int i = 1; i < N; i++)
        fact[i] = fact[i - 1] * i % mod;
    int n, m;
    cin >> n >> m;
    int res = 0;
    for (int i = 2; i < n; i++)
        res += C(n - 2, i - 2) % mod * (n-i) % mod , res %= mod;
    cout << res*C(m, n - 1) % mod<< "\n";
    return 0;
}

1278C. Berry Jam

很经典的套路,\(x_l,y_l,x_r,y_r\)分表表示 1、2 的前缀后缀的数量,求满足\(x_l+x_r=y_l+y_r\)\(l,r\)\(r-l-1\)的最小值。可以轻松的转换为\(x_l-y_l=y_r-x_r\),维护\(y_r-x_r\)对应\(r\)的最小值,然后枚举\(l\)即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

void solve() {
    int n;
    cin >> n;
    vector<int> a(2 * n + 1);
    for (int i = 1; i <= n * 2; i++) cin >> a[i];
    map<int, int> cnt;
    cnt[0] = n*2 + 1;
    for (int i = n * 2, x = 0, y = 0; i > n; i--) {
        if (a[i] == 1) x++;
        else y++;
        cnt[y - x] = i;
    }
    int res = cnt[0] - 1;
    for( int i = 1 , x = 0 , y = 0 ; i <= n ; i ++ ){
        if( a[i] == 1 ) x ++;
        else y ++;
        if( cnt.count(x-y) == 0 ) continue;
        res = min( res , cnt[x-y] - i - 1 );
    }
    cout << res << "\n";
}

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

1217C. The Number Of Good Substrings

首先,如果在没有前导零的情况下,当\(r-l+1\ge 18\)\(f(l,r)>2e5\)一定成立,所以我们在枚举\(r-l+1\)时最多只枚举 18 位即可。如果我们枚举了\(l\)表示最高位的 1 的位置,如果要使得\(r-l+1=f(l,r)\)则大多数时候需要补前导 0,所以要预处理出\(l\)左侧的一个 1 的位置,然后就可计算出最多可以补的前导零。

#include <bits/stdc++.h>

using namespace std;

#define int long long

void solve() {
    string s;
    cin >> s;
    int n = s.size() , res = 0;
    vector<int> lst(n+1);
    for( int i = 1 ; i <= n ; i ++ ){
        lst[i] = lst[i-1];
        if( s[i-1] == '1' ) lst[i] = i;
    }
    for( int r = 1 , cnt ; r <= n ; r ++ ){
        cnt = 0;
        for( int l = r ; l >= 1 and r - l < 20 ; l -- ){
            if( s[l-1] == '0' ) continue;
            cnt += 1ll << ( r - l );
            if( cnt <= r - lst[l-1] ) res ++ ;
        }
    }
    cout << res << "\n";
    return;
}

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

1260C. Infinite Fence

在保证\(r<b\)的情况下,最优的染色方案是\(\dots,n\cdot b , m\cdot r,(m+1)\cdot r,\dots,(n+1)\cdot b,\dots\)

此时在两个蓝色格子之间的红色格子数为$x=\left \lceil \frac{(n+1)b-1-nb}{r} \right \rceil =\left \lceil \frac{b-1}{r}\right \rceil $

所以要保证\(x\le k-1\),但是打表发现并不是所有情况都满足。

因为当\(\gcd(r,b)\ne 1\)时,两个蓝色格子的间距不一定是\(b-1\)

现在我们考虑什么情况下才可能会取到\(x\),会发现只有当\(mr = nb+1\)时才能取到,即\(mr-nb=1\)有解时才能取到最大值。根据裴蜀定理只有当\(gcd(r,b)=1\)时才有解。所以当\(\gcd(r,b)\ne 1\)时让\(r,b\)分别除以\(\gcd\)再计算即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

void solve() {
    int a, b, k, d;
    cin >> a >> b >> k, d = gcd(a, b);
    a /= d, b /= d;
    if (a > b) swap(a, b);
    if( ( a + b - 2 ) / a  <= k - 1 ) cout << "OBEY\n";
    else cout << "REBEL\n";
    return;
}

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

1400B. RPG Protagonist

保证剑比斧子轻,显然我们要尽可能多的选择剑。由于有两个人,所以我们要枚举出第一个人拿多少吧剑,剩下的就贪心的选择就好了。

#include <bits/stdc++.h>

using namespace std;

#define int long long

using pii = pair<int, int>;

const int inf = 1e15;

void solve() {
    int p, f, cntS, cntW, s, w, res = 0;
    cin >> p >> f >> cntS >> cntW >> s >> w;
    if (s > w) swap(s, w), swap(cntS, cntW);
    for (int a = 0, b, c, d; a <= min(cntS, p / s); a++) {
        b = min(cntW, (p - a * s) / w);
        c = min(cntS - a, f / s);
        d = min(cntW - b, (f - c * s) / w);
        res = max(res, a + b + c + d);
    }
    cout << res << "\n";
    return ;
}

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int TC;
    for (cin >> TC; TC; TC--)
        solve();
    return 0;
}

1202B. You Are Given a Decimal String...

首先我们可以枚举出\(x,y\),然后预处理出数字\(i,j\)之间的需要经过多少次操作。然后遍历字符串即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

using pii = pair<int, int>;

const int inf = 1e15;

string s;
int n;

int calc(int x, int y) {
    vector g(10, vector(10, inf));
    for (int i = 0; i < 10; i++)
        g[i][(i + x) % 10] = g[i][(i + y) % 10] = 1;
    for (int k = 0; k < 10; k++)
        for (int i = 0; i < 10; i++)
            for (int j = 0; j < 10; j++)
                g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
    int ans = 0;
    for (int i = 0, x, y; i + 1 < n; i++) {
        x = s[i] - '0', y = s[i + 1] - '0';
        if (g[x][y] == inf)return -1;
        ans += g[x][y] - 1;
    }
    return ans;
}

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> s, n = s.size();
    for (int i = 0; i < 10; i++)
        for (int j = 0; j < 10; j++)
            cout << calc(i, j) << " \n"[j == 9];

    return 0;
}

1132C. Painting the Fence

可以想到的是,枚举删除两个人。现在考虑如何快速计算剩下的。

首先我们可以每一删除的一个人。然后暴力的删掉。然后我们统计出剩下的多少个格子被染色,统计\(b[i]\)表示格子\(i\)只被染色一次。这样我们枚举第二个人后,就可以知道\([l,r]\)之间有多少个格子只被染了一次,这一部分也不被染色。快速求区间和用前缀和解决。

#include <bits/stdc++.h>

using namespace std;

using pii = pair<int, int>;

const int inf = 1e15;

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int n, q;
    cin >> n >> q;
    vector<int> a(n + 1), l(q+1), r(q + 1);
    for (int i = 1; i <= q; i++) {
        cin >> l[i] >> r[i];
        for (int j = l[i]; j <= r[i]; j++) a[j]++;
    }
    int res = 0;
    for (int i = 1, sum; i <= q; i++) {
        for (int j = l[i]; j <= r[i]; j++) a[j]--;
        vector<int> b(n + 1);
        sum = 0;
        for (int j = 1; j <= n; j++)
            b[j] = b[j - 1] + (a[j] == 1), sum += a[j] > 0 ;
        for (int j = 1; j <= q; j++)
            if (i != j) res = max(res, sum - b[r[j]] + b[l[j] - 1]);
        for (int j = l[i]; j <= r[i]; j++) a[j]++;
    }
    cout << res << "\n";
    return 0;
}

632C. The Smallest String Concatenation

这题现在肯定没有 1700

#include <bits/stdc++.h>

using namespace std;

int32_t main() {
    ios::sync_with_stdio(0), cin.tie(0);
    int n;
    cin >> n;
    vector<string> s(n);
    for( auto & i : s ) cin >> i;
    sort( s.begin(), s.end() , [](string x , string y ){
        return x + y < y + x;
    });
    for( auto i : s )
        cout << i;
    return 0;
}

678D. Iterated Linear Function

\[g(x) = x \\ g^1(x) = Ax+B\\ g^2(x)=A(Ax+B)+B = A^2x+AB+B\\ g^3(x) =A(A^2x+AB+B)+B=A^3+A^2B+AB+B\\ \vdots\\ g^n(x)=A^nx+A^{n-1}B+A^{n-2}B+\dots+B \]

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int mod = 1e9 + 7;
using i64 = long long;

int power(int x, int y) {
    int ans = 1;
    for (x = (x % mod + mod) % mod; y;) {
        if (y & 1) ans = ans * x % mod;
        x = x * x % mod, y >>= 1;
    }
    return ans;
}

int inv(int x) {
    return power(x, mod - 2);
}


int32_t main() {
    int a, b, n, x;
    cin >> a >> b >> n >> x;
    if (a == 1) cout << ( x + (n % mod) * (b % mod) % mod) % mod;
    else cout << (power(a, n) * x % mod + (b - b * power(a, n) % mod + mod) % mod * inv(1 - a) % mod) % mod;
    return 0;
}