2019-2020 ICPC Southeastern European Regional Programming Contest (SEERC 2019) 补题记录

A.max or min
CF上ac了洛谷wa1?
好神啊不会做
去膜了下官方题解 并跟着写了一发线段树 结果写挂了没调出来
后来换珂朵莉树T爆了
bzy:set维护线段不是很常见的操作吗 你怎么写到100行的
我:。。。。。

做法如下
注意到有意义的只是当前数字与目标数字的大小关系
我们可以令小于当前数字的数全部变成-1 大于则为1 等于为0
考虑以下情况
如果序列是0 1 1 1 或者0 -1 -1 -1 要让他们变成0只要进行(n-(0的个数))次操作即可
如果是0 -1 1 -1 这样的交替序列 如果 -1 1 -1 1 这样的交替序列长为L
我们要先让他们全部变成1或者-1 再全部变成0
于是操作次数就多了L/2向下取整次
我们记sumz为一共要多操作的次数 则答案为n-目标数字的出现次数+sumz
我们要维护每个交替串的起点终点和长度
对于不是环的情况求出sumz 对于是环的情况就可以特判一下1和n位置能不能形成交替串 如果能就更新一下答案
通过set可以在log时间内找出某个位置所属交替串的起点终点
然后我们要实现upd(pos,val)操作 把数组pos的位置修改 为val
注意到val若为-1 则之前pos位置必为0 若val为0 则pos位置之前必为1
这样就可以找出交替串 并且一点一点更改目标数值求出所有答案了

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 7;
typedef pair<int, int> pii;
set<pii, less<pii> > s;
#define IT set<pii, less<pii> > :: iterator
int a[maxn], n, m, sumz, ans;
vector<int> _[maxn];
void upd(int pos, int val) {
    if (val == 0) {  //1改成0
        a[pos] = 0;
        IT it = s.lower_bound(make_pair(pos, pos));
        if (it == s.end() || it->first != pos) it--;
        pii u = *it; 
        s.erase(u);
        sumz -= (u.second - u.first + 1) / 2;
        if (u.first < pos) {
            s.insert(make_pair(u.first, pos-1));
            sumz += (pos - u.first) / 2;
        }
        if (u.second > pos) {
            s.insert(make_pair(pos + 1, u.second));
            sumz += (u.second - pos) / 2;
        }
    } else if (val == -1) {//0改-1 接起来两段
        a[pos] = -1; 
        if (pos < n && a[pos + 1] == 1) {
            IT it = s.lower_bound(make_pair(pos + 1, pos + 1));
            if (it == s.end() || it->first != pos + 1) it--;
            pii u = *it;
            s.erase(u); 
            sumz -= (u.second-u.first + 1) / 2;
            s.insert(make_pair(pos, u.second));
            sumz += (u.second - pos + 1) / 2;
        } else s.insert(make_pair(pos, pos));
        if (pos > 1 && a[pos - 1] == 1) {
            IT it = s.lower_bound(make_pair(pos-1, pos-1)), it2 = s.lower_bound(make_pair(pos, pos));
            if (it == s.end() || it->first != pos - 1) it--;
            //if (it2 == s.end() || it2->first != pos) it2--;
            pii u = *it, v = *it2;
            sumz -= (u.second - u.first + 1) / 2; 
            sumz -= (v.second - v.first + 1) / 2;
            s.erase(u); 
            s.erase(v);
            sumz += (v.second - u.first + 1) / 2;
            s.insert(make_pair(u.first, v.second));
        }
    }
}
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        _[a[i]].push_back(i);
        a[i] = 1;
        s.insert(make_pair(i, i));
    }
    for (int i = 1; i <= m; i++) {
        if (_[i].size() == 0) {
            puts("-1");
            continue;
        }
        for (int j = 0; j < _[i].size(); j++) upd(_[i][j], 0);
        if (a[1] == 0 || a[n] == 0 || a[1] == a[n]) {
            ans = sumz + n - _[i].size();
        } else {
            IT it = s.lower_bound(make_pair(n, n)), it1 = s.lower_bound(make_pair(1,1));
            if (it == s.end() || it->first != n) it--;
            //if (it1 == s.end() || it1->first != 1) it1--;
            ans = sumz - (it->second-it->first+1)/2-(it1->second-it1->first+1)/2+(it1->second-it1->first+it->second-it->first+2)/2 + n - _[i].size();
        }
        cout << ans << "\n";
        for (int j = 0; j < _[i].size(); j++) upd(_[i][j], -1);
    }
}

B.level up
考场没有注意到要通过排序保证正确性
当一个X=u的元素可以使level1的经验溢出时,所有X>=u的元素都可以
我们要想办法遍历所有可以使其溢出的元素 从中选出最优解
那么按x的大小排序再进行背包就行了
dp方程就是个背包 第一维滚动数组滚掉就行

#include<bits/stdc++.h>
//wa 7
//j + e[i].x >= s1要判
//j == s1 要判
using namespace std;
const int maxn = 507;
#define ll long long
#define inf 9999999999999999ll
ll dp[2][maxn][maxn];
ll s1, s2, n;
ll ans;
struct _ {
    ll x, t, y, r;
    bool operator < (const _ &rhs) const {
        return x < rhs.x;
    }
}e[maxn];
int main() {
    cin >> n >> s1 >> s2;
    for (int i = 1; i <= n; i++) {
        cin >> e[i].x >> e[i].t >> e[i].y >> e[i].r;
    }
    sort (e + 1, e + n + 1);
    for (int i = 0; i <= s1; i++) {
        for (int j = 0; j <= s2; j++) 
            dp[0][i][j] = dp[1][i][j] = inf;
    }
    dp[0][0][0] = 0;
    for (int i = 1; i <= n; i++) {
        dp[(i&1)^1][0][0] = 0;
        for (int j = 0; j <= s1; j++) {
            for (int k = 0; k <= s2; k++) {
                if (j < s1 && j + e[i].x > s1) {
                    dp[i&1][s1][min(s2,k+j+e[i].x-s1)] = min(dp[i&1][s1][min(s2,k+j+e[i].x-s1)], min(dp[(i&1)^1][j][k]+e[i].t, dp[(i&1)^1][s1][min(s2,k+e[i].x+j-s1)]));
                } else if (j + e[i].x <= s1) {
                    dp[i&1][j+e[i].x][k] = min(dp[i&1][j+e[i].x][k], min(dp[(i&1)^1][j+e[i].x][k], dp[(i&1)^1][j][k]+e[i].t));
                } 
                if (k < s2) {
                    dp[i&1][j][min(k+e[i].y, s2)] = min(dp[i&1][j][min(k+e[i].y, s2)], min(dp[(i&1)^1][j][k] + e[i].r, dp[(i&1)^1][j][min(k+e[i].y, s2)]));
                }
            }
        }
    }
    if (dp[n&1][s1][s2] == inf) cout << -1 << endl;
    else cout << dp[n&1][s1][s2] << endl;
}

D. Cycle String?
签到题但是我wa19了
分类讨论一下 统计出现次数最多的字符的出现次数 记为mx
如果mx > 2n-2 无论如何都有长为n的重复子串了
如果mx == 2n-2且一共只有2种字符 没有合法的构造方案
如果mx <= n 直接按字典序排序输出即可
如果mx>n且不为上述情况 考虑构造解
我wa33的解的形式应为:先输出n个出现次数最多的字符 然后放一个其他字符 剩下的再输出所有出现次数最多的那个字符 然后剩下的再输出
----------------
wa33破案了
n=1没有特判
是判断的先后顺序出了问题
e.g ab是可以输出的
---------------
然后我wa35了。。。
n=2的情况 abba是合法的
---------------
wa34 这两个东西输出不能直接输出原串。。。
---------------
wa34 n==2的特判也不是按字典序输出 要mx == n才行
---------------
ac

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e6 + 7;
    int cnt[26], cnta, mx, mxi;
    string s;
    int main() {
        cin >> s;
        int l = s.length(), n = l / 2;
        for (int i = 0; i < l; i++) {
            cnt[s[i] - 'a'] ++;
            if (cnt[s[i]-'a'] == 1) cnta++;
            if (mx <= cnt[s[i]-'a']) {
                mxi = s[i]-'a';
                mx = cnt[mxi];
            }
        }
        //printf("%d\n", cnta);
        if (n <= 2 && cnta == 2 && mx == n) {
            puts("YES");
            for (int i = 0; i < 26; i++) {
                while (cnt[i]) {
                    putchar(i+'a');
                    cnt[i]--;
                }
            }
            return 0;
        } 
        if (mx >= l - 1 || (mx == l - 2 && cnta == 2)) {
            puts("NO");
            return 0;
        } else if (mx <= n) {
            puts("YES");
            for (int i = 0; i < 26; i++) {
                while (cnt[i]) {
                    putchar(i+'a');
                    cnt[i]--;
                }
            }
        } else {
            puts("YES");
            for (int i = 0; i < 26; i++) {
                if (i == mxi) continue;
                if (cnt[i]) {
                    putchar(i +'a');
                    cnt[i]--;
                    break;
                }
            }
            for (int i = 1; i <= n; i++) {
                putchar(mxi+'a');
            }
            cnt[mxi] -= n;
            for (int i = 0; i < 26; i++) {
                if (i == mxi) continue;
                if (cnt[i]) {
                    putchar(i +'a');
                    cnt[i]--;
                    break;
                }
            }
            while(cnt[mxi]) {
                putchar(mxi + 'a');
                cnt[mxi]--;
            }
            for (int i = 0; i < 26; i++) {
                //if (i == mxi) continue;
                while (cnt[i]) {
                    putchar(i+'a');
                    cnt[i]--;
                }
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值