Educational Codeforces Round 159 (Div. 2) A~E

A.Binary lmbalance(思维)

题意:

给出一个01字符串,你可以对字符串进行无限次下列操作:

  • 选择一个位置i(1≤i≤∣s∣−1,|s|为字符串s的长度)i(1 \le i \le |s| - 1,\text{|s|为字符串s的长度})i(1is1,|s|为字符串s的长度)

    • 如果s[i]≠s[i+1]s[i] \ne s[i + 1]s[i]=s[i+1],在s[i]s[i]s[i]s[i+1]s[i + 1]s[i+1]之间插入一个'0'

    • 如果s[i]=s[i+1]s[i] = s[i + 1]s[i]=s[i+1],在s[i]s[i]s[i]s[i+1]s[i + 1]s[i+1]之间插入一个'1'

问:能否在经过一些操作后,使得字符串中0的数量严格大于1的数量。

分析:

只要字符串中同时存在01,那么就可以无限产生0,此时必然成立,如果字符串中没有1,那么也必然成立。

只会当字符串中只有1时,才无法使0的数量严格大于1的数量。

代码:

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 3e5 + 5;


void solve() {
    int n;
    string s;
    cin >> n >> s;
    int one = 0, zero = 0;
    for (int i = 0; i < n; i++) {
        if (s[i] == '0') zero++;
        else one++;
    }
    if (one && !zero) {//只有1出现,0没有出现才是NO
        cout << "NO" << endl;
    } else {
        cout << "YES" << endl;
    }
}

int main() {
    int Case;
    cin >> Case;
    while (Case--) {
        solve();
    }
    return 0;
}

B.Getting Points(数学)

题意:

nnn天时间可以学习(第一天为周一),每天可以去上课,上课将获得lll点学分,每周一会布置一个课后练习,完成练习可以获得ttt点学分。

每天可以选择休息或去上课,如果选择休息,那么无法得到当天的学习学分,且不能完成课后练习。如果选择去上课,可以获得上课的学分,且每天去上课可以在未完成的练习中选择不超过两个练习任务完成。

问:需要获得PPP点学分才能毕业,最多可以休息多少天。

分析:

分两种情况进行考虑:

  • 1.如果每次去上课均完成两个任务,且这样就能获取足够的学分,那么就计算最少需要去上课几天。

  • 2.如果仅用最少上课天数去完成任务无法达到毕业学分,那么就需要额外的时间去上课,计算所需的额外天数。

题意:

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 3e5 + 5;


void solve() {
    LL n, l, t, P;
    cin >> n >> P >> l >> t;
    LL task_cnt = n / 7 + (n % 7 != 0);//计算练习任务的数量
    LL lesson_cnt = (task_cnt + 1) / 2;//计算完成练习任务所需的天数
    LL earn = task_cnt * t + lesson_cnt * l;//每次去上课都可以获得上课的学分以及任务的学分
    if (earn >= P) {//此时已经拿够学分了
        LL one_earn =  t * 2 + l;//计算一天可以获得的学分
        LL ans = P / one_earn;//计算所需的天数
        if (ans * one_earn < P) {
            ans++;
        }
        cout << n - ans << endl;
    } else {
        P -= earn;//计算还需要获得的学分
        LL ans = lesson_cnt + P / l + (P % l != 0);//此时每天只能获得上课的学分,计算额外的天数
        cout << n - ans << endl;
    }
}

int main() {
    int Case;
    cin >> Case;
    while (Case--) {
        solve();
    }
    return 0;
}

C.Insert and Equalize(GCD)

题意:

给出一个包含nnn个数字的数组aaa,其中每个数字均不相同。

你需要在数组aaa中插入一个数组aaa中没有的数字,然后选择一个xxx,对数组中每个数通过加上若干次xxx后,使得所有数字均相同。

问:最少需要多少次操作,才能使得数组中所有数字均相同?

分析:

aaa数组进行排序(此时数据存储于下标1∼n1 \sim n1n

a[n]a[n]a[n]为操作完最终的数,为了使所有数字均能通过加上xxx变为a[n]a[n]a[n],且操作次数最少,那么需要对aaa数组所有数变为a[n]a[n]a[n]所需加上的值取GCD(最大公约数)。

然后考虑插入的新数字怎么选择,考虑如果选择最大的数字加上xxx的值做为新的数字,那么此时的操作次数需要加上nnn(原数组中nnn个数字还需再加上)。

而如果能选择一个满足a[n]−k×x(k<n)a[n] - k \times x(k \lt n)a[n]k×x(k<n)的一个数字,那么只需要额外kkk次操作就能使新插入的数字也变为a[n]a[n]a[n],此时操作次数一定小于前面的方案,此时的kkk可以通过for循环进行枚举获得。

代码:

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 3e5 + 5;

int n;
LL a[N];

void solve() {
   cin >> n;
   for (int i = 1; i <= n; i++) {
       cin >> a[i];
   }
   sort (a + 1, a + n + 1);
   LL x = a[n] - a[1];
   for (int i = 2; i <= n; i++) {
       x = __gcd(x, a[n] - a[i]);
   }
  x = max(1ll, x);//考虑n为1时的情况
   LL cnt = 0;
   for (int i = 1; i <= n; i++) {
       cnt += (a[n] + x - a[i]) / x;
   }
   LL ans = cnt;
   for (int i = n - 1, k = 1; i >= 1; i--, k++) {
       if (a[i] != a[n] - k * x) {//找最小的k
           ans = min(ans, cnt - n + k);
           break;
       }
   }
   cout << ans << endl;
}

int main() {
    int Case;
    cin >> Case;
    while (Case--) {
        solve();
    }
    return 0;
}

D. Robot Queries(思维+STL)

题意:

有一个无限的二维网格。一开始,机器人站在 (0,0)(0,0)(0,0)点。机器人可以执行四条指令:
U,D,L,RU,D,L,RU,D,L,R分别表示向上下左右走一步。现在给你一个由UDLRUDLRUDLR构成的操作序列sss,以及qqq次查询,每次查询给出x,y,l,rx,y,l,rx,y,l,r,表示将s[l]−s[r]s[l]-s[r]s[l]s[r]这段操作序列翻转。
询问机器人在执行命令序列sss时是否访问了点 (x,y)(x,y)(x,y)

分析:

序列不翻转的情况下,走到每一个字母对应的位置一定是固定的,这样的情况查询每个点有没有被经过只要记录一下即可,如果进行了区间翻转,111l−1l-1l1 以及 rrrnnn 的位置仍然是不变的,只有 lllrrr 之间进行了中心翻转,通过画图可以发现如果(x,y)(x,y)(x,y)翻转之后的坐标为(sx[l−1]+sx[r]−x,sy[l−1]+sy[r]−y)(sx[l-1]+sx[r]-x,sy[l-1]+sy[r]-y)(sx[l1]+sx[r]x,sy[l1]+sy[r]y),只要查询这个点所记录的所有达到这个点的位置是否在(l,r)(l,r)(l,r)之间即可

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
int sx[200005], sy[200005];

int main() {
    int n, q;
    cin >> n >> q;
    string s;
    cin >> s;
    map<pair<int, int>, vector<int>> mp;
    int x = 0, y = 0;
    sx[0] = 0, sy[0] = 0;
    mp[{x, y}].push_back(0);
    for (int i = 0; i < n; i++) {
        if (s[i] == 'U') {
            y++;
        }
        if (s[i] == 'D') {
            y--;
        }
        if (s[i] == 'R') {
            x++;
        }
        if (s[i] == 'L') {
            x--;
        }
        mp[{x, y}].push_back(i + 1);
        sx[i + 1] = x, sy[i + 1] = y;
    }
    while (q--) {
        int x, y, l, r;
        cin >> x >> y >> l >> r;
        int ok = 0;
        if (mp[{x, y}].size() >= 1) {
            if (mp[{x, y}][0] < l || mp[{x, y}][mp[{x, y}].size() - 1] >= r) {
                ok = 1;
                cout << "YES" << endl;
                continue;
            }
        }
        int xx = sx[l - 1] + sx[r] - x;
        int yy = sy[l - 1] + sy[r] - y;
        if (mp[{xx, yy}].size() >= 1) {
            auto k = lower_bound(mp[{xx, yy}].begin(), mp[{xx, yy}].end(), l);
            if (k != mp[{xx, yy}].end() && *k < r)
                ok = 1;
        }
        if (ok == 1) {
            cout << "YES" << endl;
        } else
            cout << "NO" << endl;
    }
    return 0;
}

E. Collapsing Strings (字典树)

题意:

nnn个字符串s[i]s[i]s[i]∣s[i]∣\vert s[i]\verts[i]表示s[i]s[i]s[i]的长度,两个字符串的合并C(a,b)C(a,b)C(a,b)的运算如下:

  • 如果 aaa为空,则 C(a,b)=bC(a,b)=bC(a,b)=b
  • 如果 bbb为空,则 C(a,b)=aC(a,b)=aC(a,b)=a
  • 如果aaa的最后一个字母等于 bbb 的第一个字母,则 C(a,b)=C(a1,∣a∣−1,b2,∣b∣)C(a,b)=C(a_1,|a|-1,b_2,|b|)C(a,b)=C(a1,a1,b2,b) ,其中 sl,rs_{l,r}sl,rssslll 字母到 rrr 字母的子串;
  • 否则为 C(a,b)=a+bC(a,b)=a+bC(a,b)=a+b,即两个字符串的连接。

询问∑i=1n∑j=1n∣C(si,sj)∣\sum_{i=1}^{n}\sum_{j=1}^{n} \vert C(s_i,s_j) \verti=1nj=1nC(si,sj)

分析:

每一个字符串都要和其他包括自己的所有字符进行CCC操作,对于任何一个固定的字符串sss,它要和所有的字符串进行拼接,也就是要减去最长相等前后缀的贡献。用字典树存所有的前缀的个数。枚举所有字符串后缀从长往短搜,每次先加上当前字符串总长,再考虑容斥从答案中减去重复的答案。
记录前一个后缀匹配前缀的个数lstlstlst,当前后缀匹配前缀的个数curcurcur,以当前后缀作为匹配后缀减去的个数为 cur−lstcur-lstcurlst,答案减去这一部分即可。

代码:

#include <bits/stdc++.h>

using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int ch[N][26], cnt[N], idx;

void insert(string s) {
    int p = 0;
    for (int i = 0; i < s.size(); i++) {
        int j = s[i] - 'a'; // 字母映射
        if (!ch[p][j])
            ch[p][j] = ++idx;
        p = ch[p][j];
        cnt[p]++;
    }
}

vector<int> query(string s) {
    vector<int> v(s.size(), 0);
    int p = 0;
    for (int i = 0; i < s.size(); i++) {
        int j = s[i] - 'a';
        if (!ch[p][j])
            break;
        p = ch[p][j];
        v[i] = cnt[p];
    }
    return v;
}

int main() {
    int n;
    cin >> n;
    vector<string> s(n);
    int tot = 0;
    for (int i = 0; i < n; i++) {
        cin >> s[i];
        insert(s[i]);
        tot += s[i].size();
    }
    LL ans = 0;
    for (int i = 0; i < n; i++) {
        reverse(s[i].begin(), s[i].end());
        int len = s[i].size();
        ans += tot + 1LL * len * n;
        int lst = 0;
        auto v = query(s[i]);
        for (int j = s[i].size() - 1; j >= 0; j--) {
            int cur = v[j];
            int ad = cur - lst;
            lst = cur;
            ans -= 2 * (j + 1) * ad;
        }
    }
    cout << ans << endl;
    return 0;
}

学习交流

以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值