Codeforces Round #750 (Div. 2)

本文探讨了Codeforces编程竞赛中不同题目的解题思路,包括贪心策略、简单计数方法、回文串判断、构造序列以及动态规划。通过对具体问题的分析,展示了如何运用这些算法解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Codeforces Round #750 (Div. 2)

Dashboard - Codeforces Round #750 (Div. 2) - Codeforces

A. Luntik and Concerts

思路

  • 贪心

​ 由于 a , b , c ≥ 1 a,b,c\ge 1 a,b,c1,因此 3 3 3的影响一定可以被 1 , 2 1,2 1,2抵消掉,因此就是看剩下的 1 , 2 1,2 1,2是否可以均分,因此左右的差不会超过 1 1 1,贪心的来想如果可以为 0 0 0则一定有一种解可以让其 均分,因此直接输出 ( a + b × 2 + c × 3 ) % 2 (a+b\times 2+c\times 3)\%2 (a+b×2+c×3)%2即可。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
LL a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> a[1] >> a[2] >> a[3];
        cout << (a[1] + a[2] * 2 + a[3] * 3) % 2 << '\n';
    }
    return 0;
}
// 5 1 2 2
// 1 2 2
// 3  1 2
// 

B. Luntik and Subsequences

思路

  • 简单计数

对于 1 1 1 选一个即可,对于每一个 0 0 0 可选可不选,因此方案为: c n t 1 × 2 c n t 0 cnt_1\times 2^{cnt_0} cnt1×2cnt0

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        int cnt1 = 0, cnt0 = 0;
        for (int i = 1; i <= n; i ++) {
            cin >> a[i];
            if (a[i] == 1) cnt1 ++;
            else if (!a[i]) cnt0 ++;
        }

        cout << ((LL)cnt1 * (1ll << cnt0)) << '\n';
        
    }
    return 0;
}

C. Grandma Capa Knits a Scarf

思路

  • 暴力,贪心

暴力枚举将 [ ′ a ′ , ′ z ] ['a','z] [a,z]都删掉的情况,然后判断剩下的是否是回文串,如果可以的话将他们的位置提取出来,得到一个一个的间隔,由于尽量少删,因此尽量在间隔中对称的合法加入即可。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
bool check(string t) {
    int l = 0, r = t.size() - 1;
    while (l < r) {
        if (t[l] != t[r]) return false;
        l ++, r --;
    }
    return true;
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        string str; cin >> str;
        int res = INF;
        for (int j = 0; j < 26; j ++) {
            string t = "";
            int cnt = 0;
            vector<int> pos, has;
            vector<bool> st(n + 1); 
            for (int i = 0; i < n; i ++)
                if (str[i] - 'a' != j)
                    t += str[i],  has.push_back(i + 1);
                else cnt ++, st[i + 1] = true;
            if (check(t)) {
                int l = 0, r = t.size() - 1;
                int lastl = 0, lastr = n + 1;
                while (l <= r) {
                    int lcnt = 0, rcnt = 0;
                    for (int i = lastl + 1; i <= has[l]; i ++) 
                        if (st[i]) lcnt ++;
                    for (int i = has[r]; i < lastr; i ++)
                        if (st[i]) rcnt ++;
                    cnt -= min(lcnt, rcnt) * 2;
                    lastl = has[l], lastr = has[r];
                    l ++, r --;
                }
                if (t.size() % 2 == 0 && t.size()) {
                    l --, r ++;
                    int ttt = 0;
                    for (int i = has[l]; i <= has[r]; i ++) 
                        if (st[i]) ttt ++;
                    cnt -= ttt;
                }
                res = min(res, cnt);
            }
        }

        cout << (res == INF ? -1 : res) << '\n';
    }
    return 0;
}

D. Vupsen, Pupsen and 0

思路

  • 构造

从小样例入手,例如只有 1 , 2 1,2 1,2 则我们将他们调换且其中一个加负号即可,对于长一些的序列我们将其转化成小样例的情况,即如果是偶数则两两合并即可,如果是奇数则最后剩三个,将其中两个合并然后和剩余的构成一个对(注意这里要保证两个数合并后和不为 0 0 0,因为 b b b不能为 0 0 0,直接暴力枚举所有情况选一个合法的即可)。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
PII a[N];
int sgn(int x) {
    if (x > 0) return 1;
    return 0;
}
PII g(int x, int y) {
    if (sgn(x) == sgn(y)) return {y, -x};
    else return {abs(y), abs(x)};
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i].x, a[i].y = i;

        vector<int> res(n + 1);
        if (n % 2 == 0) {
            for (int i = 1; i <= n; i += 2) {
                PII t = g(a[i].x, a[i + 1].x);
                res[i] = t.x, res[i + 1] = t.y;
            }
        }
        else {
            for (int i = 1; i + 1 <= n - 3; i += 2) {
                PII t = g(a[i].x, a[i + 1].x);
                res[i] = t.x, res[i + 1] = t.y;
            }
            if (a[n - 2].x + a[n - 1].x) {
                PII t = g(a[n - 2].x + a[n - 1].x, a[n].x);
                res[n - 2] = t.x, res[n - 1] = t.x, res[n] = t.y;
            }
            else if (a[n - 2].x + a[n].x) {
                PII t = g(a[n - 2].x + a[n].x, a[n - 1].x);
                res[n - 2] = t.x, res[n - 1] = t.y, res[n] = t.x;
            }
            else {
                PII t = g(a[n].x + a[n - 1].x, a[n - 2].x);
                res[n - 2] = t.y, res[n - 1] = t.x, res[n] = t.x;
            }
        }
       

        for (int i = 1; i <= n; i ++) cout << res[i] << " \n"[i == n];
    }
    return 0;
}

E. Pchelyonok and Segments

题意

​ 给你一个 n n n的数组 a a a,让你选择一个最大的 k k k,且满足你可以从左到右选择 k k k个区间,满足第 i i i区间 满足 [ l i , r i ] [l_i,r_i] [li,ri]的长度为 k − i + 1 k-i+1 ki+1 i ∈ [ 1 , k − 1 ]    s i < s i + 1 i\in [1,k-1]\ \ s_i<s_{i+1} i[1,k1]  si<si+1

思路

  • 暴力 d p dp dp

​ 由于要满足 ( k + 1 ) × k / 2 ≤ n (k+1)\times k/2 \le n (k+1)×k/2n,因此 k k k n \sqrt n n 级别的,所以考虑 O ( n n ) O(n\sqrt n) O(nn ) d p dp dp。发现当前的需要用到后面的状态,因此考虑从后往前 d p dp dp,设 f [ i ] [ j ] f[i][j] f[i][j] [ i , n ] [i,n] [i,n]最左边连续选 j j j个的最大区间和是多少,它的后继按照选不选第 i i i个来划分,即 f [ i + 1 ] f[i+1] f[i+1] ∑ k = i i + j − 1 a k \sum_{k=i}^{i+j-1}a_k k=ii+j1ak(由于题目要求要满足 ∑ k = i i + j − 1 a k < f [ i + j ] [ j − 1 ] \sum_{k=i}^{i+j-1}a_k<f[i+j][j-1] k=ii+j1ak<f[i+j][j1]),最好检查一下 f [ 1 ] [ k ] f[1][k] f[1][k]是否大于 0 0 0即可。

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
LL s[N];
int f[N][500];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i], s[i] = s[i - 1] + a[i];        
        // (1 + i) * i <= 2n
        for (int i = 1; (LL)i * (i + 1) <= n * 2; i ++) f[n + 1][i] = -INF;
        f[n + 1][0] = INF;
        for (int i = n; i; i --) {
            for (int j = 0; (LL)j * (j + 1) <= n * 2; j ++) {
                f[i][j] = f[i + 1][j];
                if (j && i + j - 1 <= n && s[i + j - 1] - s[i - 1] < f[i + j][j - 1]) f[i][j] = max(1ll * f[i][j], s[i + j - 1] - s[i - 1]);
            }
        }

        int res = 1;
        for (int i = 1; (LL)i * (i + 1)<= n * 2; i ++)  
            if (f[1][i] > 0) res = i;

        cout << res << '\n';
    }
    return 0;
}

F2. Korney Korneevich and XOR (hard version)

题意

​ 给你一个长度为 n n n的序列,请你求出它的每一个上升子序列的异或和,并找出有多少个不同的值,并从小到大输出他们。

思路

  • d p dp dp

​ 先考虑 F 1 F1 F1,由于 a i a_i ai很小 因此最后异或 出来的值域 V V V也很小,设 f [ i ] f[i] f[i]:序列异或和为 i i i且最后一个数的最小值,从前往后对于每一个 a i a_i ai 我们暴力的枚举 所有的值域如果 f [ j ] < a i f[j]<a_i f[j]<ai就可以用 a i a_i ai去更新 f [ j ⊕ a i ] f[j\oplus a_i] f[jai],即复杂度为 O ( n × V ) O(n\times V) O(n×V),这个复杂度是没法通过 F 2 F2 F2的。

​ 考虑我们的转移瓶颈在于枚举值域,因此设 u p d [ i ] upd[i] upd[i]:枚举到目前位置能更新 i i i的所有的子序列异或和的值都有什么,然后对于 a i a_i ai 等价于用 u p d [ a i ] upd[a_i] upd[ai] 存的每一个值 j j j 来和 a i a_i ai异或产生贡献,即 t = j ⊕ a i t=j\oplus a_i t=jai ,判断这个值能够更新 u p d upd upd什么,由于 t t t 是以 a i a_i ai 结尾的,因此可以用 t t t 更新 ( a i , V ] (a_i,V] (ai,V],发现这个更新具有后缀性质,如果原来用 t t t更新过 ( a i , V ] (a_i,V] (ai,V]这些位置了,那么下一次就不需要从 V V V 开始,可以从 a i a_i ai开始,所以搞个东西记录一下每个状态现在最大更新点是什么即可,因此每个值只会对应 V V V个状态,且值域有 V V V个状态,并且不会重复更新,因此这样做复杂度就降到 O ( n + V 2 ) O(n+V^2) O(n+V2)

Code

#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e6 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int suf[N];
vector<int> upd[N];
bool st[N];
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n;
    for (int i = 0; i <= 8192; i ++) upd[i].push_back(0), suf[i] = 8192;
    
    st[0] = true;
    int res = 1;
    for (int i = 1; i <= n; i ++) {
        int x; cin >> x;
        for (auto t : upd[x]) {
            int v = t ^ x;
            res += !st[v], st[v] = true;
            while (suf[v] > x) upd[suf[v] --].push_back(v);
        }

        upd[x].clear();
    }    

    cout << res << '\n';
    for (int i = 0; i <= 8192; i ++)
        if (st[i]) cout << i << ' ';

    cout << '\n';
    return 0;
}
// n % p
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值