板刷 Edu

板刷 Edu

Educational Codeforces Round 100

A.Dungeon

弱智题,但是我一眼上去不会。

一轮操作总共造成 9 点伤害,所以符合要求的一个必要条件是 9|sum,还要注意每个怪物每轮至少受到一点伤害,生命最小的不能被刮死,所以还要 min(a,b,c)sum9,两个合起来是充要的。

void solve() {
    int a, b, c; cin >> a >> b >> c;
    int mn = min({a, b, c}), sum = a + b + c;
    if(sum % 9 == 0 && mn >= sum / 9) cout << "Yes\n";
    else cout << "No\n";
}

B.Find The Array

注意到式子里有个 2,吸引人从 2 的次幂考虑。

发现可以对于每个 ai,匹配小于等于它的最大 2 的幂,这样 |aibi|<a2,所以 i=1n2|aibi|<S,符合要求。

void solve() {
    cin >> n; 
    rep(i, 1, n) {int x; cin >> x; cout << (1 << __lg(x)) << " \n"[i == n];}
}

C.Busy Robot

小模拟,动态更新当前正在执行的操作,判断指令是否被忽略。注意到对于 [ti,ti+1] 这个区间,执行的操作要么完全包含这个区间,要么在这个区间中结束。所以对于每一个区间,机器人走过的区间是容易计算的。

假设当前在执行的指令开始时间、结束时间、起始位置、方向分别是 st,ed,from,d,那么对于 t[st,ed],机器人位置为 from+(tst)d,代入 ti,ti+1 计算,检查 xi 是否在这个区间内即可。注意特判 ti+1 超过 ed 的情况。

int n;
pair<ll, ll> a[N], b[N];
void solve() {
    cin >> n; rep(i, 1, n) cin >> a[i].fi >> a[i].se;
    ll ed = 0, from, to = 0, d, st;
    rep(i, 1, n) {
        if(ed <= a[i].fi) {
            from = to, to = a[i].se; ed = a[i].fi + abs(from - to), st = a[i].fi;
            d = to - from > 0 ? 1 : -1;
        }
        b[i].fi = from + d * (a[i].fi - st);
        if(i < n) b[i].se = a[i + 1].fi > ed ? to : from + d * (a[i + 1].fi - st);
        else b[i].se = to;
        if(b[i].fi > b[i].se) swap(b[i].fi, b[i].se);
    }
    int ans = 0;
    rep(i, 1, n) if(b[i].fi <= a[i].se && a[i].se <= b[i].se) ans++;
    cout << ans << "\n";
}

D.Pairs

首先有直观的贪心,对于 x 个取最小值的组合,尽量用 b 里较小的和补集里较大的匹配,剩下 nx 同理,这个可以交换证明,但很显然。

然后可以发现会有某些数必须放最小值,有些必须放最大值,这个可以双指针,但我用 set,STL 给我的自信。

int a[N], b[N], n, vis[2 * N], cnt;
void solve() {
    cin >> n; rep(i, 1, n) cin >> b[i];
    rep(i, 1, n) vis[b[i]] = 1;
    int ans1 = n, ans2 = n;
    set<int> s;
    rep(i, 1, 2 * n) if(!vis[i]) s.insert(i);
    rep(i, 1, n) {
        if(s.upper_bound(b[i]) == s.end()) break;
        ans1--; s.erase(s.upper_bound(b[i]));
    } 
    s.clear();
    rep(i, 1, 2 * n) if(!vis[i]) s.insert(i);
    req(i, n, 1) {
        if(s.lower_bound(b[i]) == s.begin()) break;
        ans2--; s.erase(prev(s.lower_bound(b[i])));
    } 
    rep(i, 1, n) vis[b[i]] = 0;
    cout << n - ans1 - ans2 + 1 << "\n";
}

E.Plan of Lectures

评价是没有 D 难,注意到 k 个特殊约束相当于把两个数捆绑在一起,判断矛盾使用带权并查集维护,权值越小表示这个数越靠后。打包好之后相当于进行了一个缩点,按照 a 的限制建图,最后跑拓扑排序即可。

三种无解的情况 :

  • 维护 k 个约束时出现矛盾,
  • 建图时和已有约束矛盾,
  • 拓扑排序时有环。

虽然写的很答辩,也调了很久,但很直观(个人而言)。

int n, k; 
int a[N], in[N];
struct node {
    int id, f, dep, siz; //dep 表示权值,dep越大,说明它在当前节点内应该越靠前
}b[N];
pii tmp[N];
int find(int x) {
    if(b[x].f == x) return x;
    int p = b[x].f;
    b[x].f = find(b[x].f);
    b[x].dep += b[p].dep;
    return b[x].f;
}
bool mer(int x, int y) {
    int xx = find(x), yy = find(y);
    if(xx != yy) b[xx].f = yy, b[xx].dep = b[yy].siz, b[yy].siz += b[xx].siz;
    xx = find(x), yy = find(y);
    if(b[x].dep - b[y].dep != 1) return 0; //合并后权值差不为 1 说明出现矛盾,不满足相邻
    return 1;  
}
vector<int> ans, p[N], e[N];
void add(int u, int v) {e[u].emplace_back(v);}
void solve() {
    cin >> n >> k; 
    rep(i, 1, n) b[i].f = i, b[i].dep = 0, b[i].id = i, b[i].siz = 1;
    rep(i, 1, n) cin >> a[i]; 
    rep(i, 1, k) {
        int x, y; cin >> x >> y;
        if(!mer(x, y)) {cout << "0\n"; return;}
    }
    rep(i, 1, n) {
        if(!a[i]) continue;
        if(find(a[i]) == find(i)) {
            if(b[a[i]].dep <= b[i].dep) {cout << "0\n"; return;} // a[i] 已经排在 i 后面,说明无解
            continue;
        }
        add(find(a[i]), find(i)), in[find(i)]++;
    }

    rep(i, 1, n) tmp[i].fi = b[i].dep, tmp[i].se = i;
    sort(tmp + 1, tmp + n + 1, greater<pii>());
    rep(i, 1, n) p[find(tmp[i].se)].push_back(tmp[i].se);
    // 维护缩点后每个点内的排列顺序

    queue<int> q;
    rep(i, 1, n) {
        if(!in[i] && find(i) == i) {
            q.push(i);
        } 
    } 
    while(q.size()) {
        int x = q.front(); q.pop();
        if(p[x].size()) ans.push_back(x);
        for(int ed : e[x]) {
            if(!--in[ed]) q.push(ed);
        }
    }
    rep(i, 1, n) if(in[i]) {cout << "0\n"; return;} //成环无解
    for(int i : ans) for(int j : p[i]) cout << j << " ";
}

F.Max Correct Set

3100 的神秘题,写不了一点,skip。

Educational Codeforces Round 101

A.Regular Bracket Sequence

垃圾翻译,原来只有一个左括号和一个右括号,直接 check 开头结尾是否合法即可。

B.Red and Blue

两个序列前缀和最大值加起来。

C.Building a Fence

想清楚式子,维护当前篱笆底边能放的最低的高度和最高的高度,check 下一个能否合法放置即可。

void solve() {
    cin >> n >> k;
    rep(i, 1, n) cin >> h[i];
    int mn = h[1], mx = h[1];
    rep(i, 2, n) {
        //h[i], h[i] + k - 1, mx + k - 1, mn + 1 - k
        if(h[i] > mx + k - 1 or h[i] + 2 * k - 2 < mn) {cout << "NO\n"; return;}
        mn = max(h[i], mn + 1 - k);
        mx = min(h[i] + k - 1, mx + k - 1);
    }
    if(mn > h[n]) cout << "NO\n";
    else cout << "YES\n";
}

D.Ceil Divisions

直观想法是把 [3,n1] 之内的数除以 n,这样剩下一个 n, 操作次数 log2n 级别,过不去。

发现 n5 次根号就变成 1,直接倒序处理,遇到 n 就把 n 除一下,这样 n 除最多 5 次,可以通过。

void solve() {
    int n; cin >> n;
    if(n <= 2) {cout << "0\n"; return;}
    int now = n;
    vector<pii> ans;
    req(i, n - 1, 2) {
        if(i == (int)sqrt(now)) now = ceil(1.0 * now / i), ans.push_back({n, i});
        if(i == 2) {
            while(now != 2) now = ceil(1.0 * now / 2), ans.push_back({n, 2});
        }
        ans.push_back({i, n});
    }
    cout << ans.size() << "\n";
    for(auto [x, y] : ans) cout << x << " " << y << "\n";
}

E.A Bit Similar

很妙。发现直接找符合条件的不好做,反过来考虑,不满足条件的序列是怎样的。

很容易发现,对于 s 的每个长度为 k 的子串,按位取反后就是一个不合法的串,一共有 nk+1 长度为 k 的子串,最坏情况下(互不相同),就会有 nk+1 个不能选,设 z=nk+1 ,显然当 2k>z 时一定有解。

考虑如何构造解,设 lg=log2(z+1),那么前面的 klg 位用不到,可以全部填 0,后面的位共有 2lg 种情况,O(n) 划窗去掉所有不合法的 ,找到剩下最小的转成二进制输出就是答案,可以开桶,也可以用 set 维护。

坑点不少:

  • 由于存在重复的情况, 2kz 时也可能有合法的解,不能直接特判掉。
  • 前面全部填 0 的部分也可能产生匹配,注意前面能匹配时这个串就合法了,不能删掉。
int n, k;
string str;
set<int> s;
void solve() {
    cin >> n >> k >> str; str = " " + str;
    int z = n - k + 1; 
    int lg = 0; while((1 << lg) <= z) lg++;
    lg = min(lg, k);
    rep(i, 0, (1 << lg) - 1) s.insert(i);
    int now = 0, cnt = 0;
    rep(i, 1, k - lg) if(str[i] == '0') cnt++;
    req(i, k, k - lg + 1) if(str[i] == '0') now += (1 << k - i); 
    if(!cnt and s.find(now) != s.end()) s.erase(now);
    rep(i, k + 1, n) {
        if((now >> lg - 1) & 1) now -= 1 << lg - 1;
        if(str[i - k] == '0') cnt--; if(str[i - lg] == '0') cnt++;
        now <<= 1;
        if(str[i] == '0') now++;
        if(!cnt and s.find(now) != s.end()) s.erase(now);
    }
    if(s.empty()) {
        cout << "NO\n";
        return;
    }
    cout << "YES\n";
    int t, v = *s.begin();
    vector<int> ans; while(v) ans.push_back(v & 1), v >>= 1;
    reverse(all(ans));
    t = k - ans.size();
    rep(i, 1, t) cout << "0"; for(auto i : ans) cout << i;
    cout << "\n";
    s.clear();
}

写的丑,常数大。

F.Power Sockets

咕咕咕。

posted @   harqwq  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示