Day 5

Day 5

今天是模拟赛啊.

早上

emm 依旧咖啡 + 面包 + 果酱 + ……

今天是凉糕欸.

……难吃, 不如哈尔滨的.

上午

???怎么总感觉这套题我做过诶.

印象不深了.

T1

给你若干条线段, 让你求 \(f(x)\) 的最大值.

\(f(x)\) 表示两条相交线段的不交部分.

这道题我绝对见过!先排序再……

赛时犯糖打了个双指针, 加上样例更糖. 所以爆 \(10pts\) 了.

T2

这题我没见过我吃!

给你若干个字符串, 你要把他们按一定顺序扔进一个集合, 当两个字符串相等或一个字符串与另一个字符串刨去第一个字符时相等.

一定是最大权独立集!

把所有前缀作为点, 冲突的建边, 然后跑最大权独立集.

赛时一开始想写字符串哈希的, 结果写到一半忘了咋写了.

无奈之下改成了 \(substr\) , \(\mathcal{O}(n \log n)\) 爆改 \(\mathcal{O}(n^2 \log n)\)

我是人?

T3

给你一个 \(01\) 串. 每次操作让其中一段排序, 或者让其中一段转化成十进制.

\(10^9 + 7\) 取模

这题好像没见过.

一眼线段树啊!

赛时用 popcount 求出 \(1\) 的个数了.

调了半天, 最后十分钟发现还在断网, 为了成功交题我开始开热点然后试图交题.

我的笔记本发力了!

没检测到热点是啥啊.

...

好的最后十分钟浪费了, 而且还没交上题. 保龄.

赛后两分钟成功提交了个暴力, 拿到了总分 \(110pts\) 的好成绩.

诸事不顺啊.

赛后十分钟把 T3 切了.

……

#include <bits/stdc++.h>
using namespace std;

namespace OI {

#define int long long
#define endl "\n"

constexpr int maxn = 2e5+5, mod = 1e9+7;

int two[maxn];

inline int fpow(int a, int b) {
    return two[b];
}

struct node {
    int val, tag = -1;
    int cnt;
} seg[maxn << 2];

void pushUp(int x, int L, int R) {
    int mid = L + R >> 1;
    seg[x].val = (seg[x << 1].val * fpow(2, R - mid) % mod) + seg[x << 1 | 1].val;
    seg[x].val %= mod;
    seg[x].cnt = seg[x << 1].cnt + seg[x << 1 | 1].cnt;
}

int a[maxn];

void build(int x, int L, int R) {
    if (L == R) {
        seg[x].val = a[L];
        seg[x].cnt = a[L];
        return;
    }
    int mid = L + R >> 1;
    build(x << 1, L, mid),
    build(x << 1 | 1, mid + 1, R);
    pushUp(x, L, R);
}

void tag(int x, int L, int R, int k) {
    if (k)
        seg[x].val = fpow(2, R - L + 1) - 1,
        seg[x].val = (seg[x].val + mod) % mod,
        seg[x].cnt = R - L + 1;
    else
        seg[x].val = 0,
        seg[x].cnt = 0;
    seg[x].tag = k;
}

void pushDown(int x, int L, int R) {
    if (seg[x].tag != -1) {
        int mid = L + R >> 1;
        tag(x << 1, L, mid, seg[x].tag),
        tag(x << 1 | 1, mid + 1, R, seg[x].tag);
        seg[x].tag = -1;
    }
}

void cover(int x, int L, int R, int l, int r, int k) {
    if (l <= L && R <= r) {
        tag(x, L, R, k);
        return;
    }
    pushDown(x, L, R);
    int mid = L + R >> 1;
    if (l <= mid)
        cover(x << 1, L, mid, l, r, k);
    if (mid < r)
        cover(x << 1 | 1, mid + 1, R, l, r, k);
    pushUp(x, L, R);
}

int query(int x, int L, int R, int l, int r) {
    if (l <= L && R <= r) {
        return seg[x].val;
    }
    pushDown(x, L, R);
    int mid = L + R >> 1;
    int ans = 0;
    if (l <= mid)
        ans = query(x << 1, L, mid, l, min(mid, r)) * fpow(2, max(0ll, r - mid)) % mod;
    if (mid < r)
        ans += query(x << 1 | 1, mid + 1, R, max(l, mid + 1), r);
    return ans % mod;
} 

int queryCnt(int x, int L, int R, int l, int r) {
    if (l <= L && R <= r) {
        return seg[x].cnt;
    }
    pushDown(x, L, R);
    int mid = L + R >> 1;
    int ans = 0;
    if (l <= mid)
        ans = queryCnt(x << 1, L, mid, l, r);
    if (mid < r)
        ans += queryCnt(x << 1 | 1, mid + 1, R, l, r);
    return ans;
}

int n, q;

void main() {
    two[0] = 1;
    for (int i = 1; i <= 2e5; i++)
        two[i] = two[i - 1] * 2 % mod;
    string str;
    cin >> str;
    n = str.size();
    for (int i = 0; i < n; i++)
        a[i + 1] = str[i] - '0';
    build(1, 1, n);
    int ans = 0;
    cin >> q;
    while (q--) {
        int opt, l, r;
        cin >> opt >> l >> r;
        int count = 0;
        switch (opt) {
            case 1:
                count = queryCnt(1, 1, n, l, r);
                if (count)
                    cover(1, 1, n, l, r, 0),
                    cover(1, 1, n, l, l + count - 1, 1);
                break;
            case 2:
                count = queryCnt(1, 1, n, l, r);
                if (count)
                    cover(1, 1, n, l, r, 0),
                    cover(1, 1, n, r - count + 1, r, 1);
                break;
            case 3:
                cout <<
                    query(1, 1, n, l, r)
                << endl;
                break;
        }
    }
}

#undef int
#undef endl

}

int main() {
    freopen("binary.in", "r", stdin);
    freopen("binary.out", "w", stdout);

    cin.tie(0), cout.tie(0);
    ios::sync_with_stdio(0);

    OI::main();

    return 0;
}

nmd

中午

去吃了碗牛肉饭, 有点辣. 还喝了点咖啡.

回来偶遇小升!

当初我算的是对的啊, 就是今天!

所谓算无遗策, 亦不过如此而已.

奶龙大人

名言

三国杀害人不浅.

下午

开始改题.

T2

现在想起来字符串哈希咋写了.

Damn it.

#include <bits/stdc++.h>
using namespace std;

namespace OI {

#define int long long
#define endl "\n"

int T, n;

struct elem {
    int len, head, hash;
};

bool cmp(elem a, elem b) {
    if (a.len == b.len)
        return a.hash < b.hash;
    return a.len < b.len;
}

constexpr int maxn = 1e6+5;
int tot;
vector<int> graph[maxn];
vector<elem> st;
map<int, int> mp;
int f[maxn][2];
bool vis[maxn];

namespace hash {
    constexpr int mod = 829194012301835;
    inline int add(int s, char c) {
        int x = s * 27 + c - 'a' + 1;
        return x % mod;
    }
    int time[maxn];
    inline int suffix(int s, int n, int head) {
        int pre = head * time[n - 1] % mod;
        return (s - pre + mod) % mod;
    }
}

void dfs(int u, int fa) {
    if (vis[u])
        return;
    vis[u] = 1;
    for (int v : graph[u]) {
        if (v == fa)
            continue;
        dfs(v, u);
        f[u][0] += max(f[v][0], f[v][1]),
        f[u][1] += f[v][0];
    }
}

void main() {
    hash::time[0] = 1;
    for (int i = 1; i <= 1e6; i++)
        hash::time[i] = hash::time[i - 1] * 27 % hash::mod;
    cin >> T;
    while (T--) {
        cin >> n;
        st.clear();
        mp.clear();
        tot = 0;
        for (int i = 1; i <= n; i++) {
            string str;
            cin >> str;
            int ins = 0;
            for (int len = 0; len < str.size(); len++) {
                ins = hash::add(ins, str[len]);
                elem element = {
                    len + 1,
                    str[0] - 'a' + 1,
                    ins
                };
                st.push_back(element);
            }
        }
        sort(st.begin(), st.end(), cmp);
        st.erase(unique(st.begin(), st.end(), [](elem a, elem b) {
            return a.hash == b.hash;
        }), st.end());
        for (int i = 1; i <= st.size(); i++)
            graph[i].clear();
        for (auto s : st) {
            mp[s.hash] = ++tot;
            int tmp = hash::suffix(s.hash, s.len, s.head);
            if (mp.count(tmp))
                graph[mp[tmp]].push_back(tot),
                graph[tot].push_back(mp[tmp]);
        }
        int ans = 0;
        for (int i = 1; i <= st.size(); i++)
            f[i][0] = 0,
            f[i][1] = 1,
            vis[i] = 0;
        for (int i = 1; i <= st.size(); i++)
            if (!vis[i])
                dfs(i, 0),
                ans += max(f[i][0], f[i][1]);
        cout << ans << endl;
    }
}

#undef int
#undef endl

}

int main() {
    freopen("set.in", "r", stdin);
    freopen("set.out", "w", stdout);

    cin.tie(0), cout.tie(0);
    ios::sync_with_stdio(0);

    OI::main();

    return 0;
}

T1

先排序是对的, 然后通过一系列转化改成最大值最小值问题.

不知道为啥我没切.

由于我对这套题十分生气, 于是我不想用题解的写法.

#include <bits/stdc++.h>
using namespace std;

namespace OI {

#define int long long
#define endl "\n"

constexpr int maxn = 2e5 + 5;
int B;

struct node {
    int l, r;
} seg[maxn];

int n;
int rnk[maxn];
int allR[maxn];
int m;

int minVal[maxn], maxVal[maxn];
int belong[maxn];
int L[maxn], R[maxn];

int minPos[maxn], maxPos[maxn];

void buildBlock() {
    int blockCnt = (m + B - 1) / B;
    for (int i = 1; i <= blockCnt; i++) {
        L[i] = (i - 1) * B + 1;
        R[i] = min(i * B, m);
        minVal[i] = LONG_LONG_MAX;
        maxVal[i] = LONG_LONG_MIN;
    }
    for (int i = 1; i <= m; i++)
        belong[i] = (i - 1) / B + 1;
}

void update(int pos, int vMin, int vMax) {
    int b = belong[pos];
    minVal[b] = min(minVal[b], vMin);
    maxVal[b] = max(maxVal[b], vMax);
}

int queryMin(int ql, int qr) {
    if (ql > qr)
        return LONG_LONG_MAX;
    int bL = belong[ql], bR = belong[qr];
    int ans = LONG_LONG_MAX;
    if (bL == bR) {
        for (int i = ql; i <= qr; i++) {
            ans = min(ans, minPos[i]);
        }
    } else {
        for (int i = ql; i <= R[bL]; i++)
            ans = min(ans, minPos[i]);
        for (int b = bL + 1; b <= bR - 1; ++b)
            ans = min(ans, minVal[b]);
        for (int i = L[bR]; i <= qr; i++)
            ans = min(ans, minPos[i]);
    }
    return ans;
}

int queryMax(int ql, int qr) {
    if (ql > qr)
        return LONG_LONG_MIN;
    int bL = belong[ql], bR = belong[qr];
    int ans = LONG_LONG_MIN;
    if (bL == bR) {
        for (int i = ql; i <= qr; i++)
            ans = max(ans, maxPos[i]);
    } else {
        for (int i = ql; i <= R[bL]; i++)
            ans = max(ans, maxPos[i]);
        for (int b = bL + 1; b <= bR - 1; ++b)
            ans = max(ans, maxVal[b]);
        for (int i = L[bR]; i <= qr; i++)
            ans = max(ans, maxPos[i]);
    }
    return ans;
}

void main() {
    cin >> n;
    B = sqrt(n);
    for (int i = 1; i <= n; i++) {
        cin >> seg[i].l >> seg[i].r;
        allR[i] = seg[i].r;
    }

    sort(seg + 1, seg + 1 + n, [](node a, node b) {
        if (a.l == b.l) return a.r < b.r;
        return a.l < b.l;
    });

    sort(allR + 1, allR + 1 + n);
    m = unique(allR + 1, allR + 1 + n) - (allR + 1);
    for (int i = 1; i <= n; i++) {
        rnk[i] = lower_bound(allR + 1, allR + 1 + m, seg[i].r) - allR;
    }

    buildBlock();
    for (int i = 1; i <= m; i++) {
        minPos[i] = LONG_LONG_MAX;
        maxPos[i] = LONG_LONG_MIN;
    }

    int ans = 0;

    for (int j = 1; j <= n; ++j) {
        int lj = seg[j].l, rj = seg[j].r;
        int posJ = rnk[j];

        int Lpos = lower_bound(allR + 1, allR + 1 + m, lj) - allR;
        if (Lpos <= posJ) {
            int minVal = queryMin(Lpos, posJ);
            if (minVal != LONG_LONG_MAX)
                ans = max(ans, (rj + lj) - minVal);
        }

        if (posJ < m) {
            int maxVal = queryMax(posJ + 1, m);
            if (maxVal != LONG_LONG_MIN)
                ans = max(ans, maxVal - (rj - lj));
        }

        int pos = posJ;
        minPos[pos] = min(minPos[pos], rj + lj);
        maxPos[pos] = max(maxPos[pos], rj - lj);
        
        int b = belong[pos];
        minVal[b] = min(minVal[b], minPos[pos]);
        maxVal[b] = max(maxVal[b], maxPos[pos]);
    }

    cout << ans << endl;
}

#undef int
#undef endl

}  // namespace OI

int main() {
    freopen("bee.in", "r", stdin);
    freopen("bee.out", "w", stdout);

    cin.tie(0), cout.tie(0);
    ios::sync_with_stdio(0);

    OI::main();

    return 0;
}

块块!

T4

这破玩意我研究一下午.

好的. (后来发现是黑题)

给你序列 \(a\), 你需要选择一个长度为 \(k\) 的子序列 \(b\) 使得:

\[\sum_{i = 1}^k i \cdot b_i \]

最大, 对于 \(\forall k \in [1, n]\) 求出这个值.

题解是 Splay 所以这题没人看也没人会.

我们细看下这道题, 首先 \(\mathcal{O}(n^2)\) 的 DP 是好想的:

\[f_j \xleftarrow{\max} f_{j - 1} + j \cdot a_i \]

\(0/1\) 背包状物.

仔细观察这个式子, 注意到这个式子是具有一定单调性的. 当我们加入一个物品 \(a_i\) 时, 我们设旧的背包为 \(f'\) 则:

\[\exists p \mid \forall j \ge p, f'_{j - 1} + j \cdot a_i \gt f'_j \]

所以我们可以通过二分找到这个 \(p\) 点.

对于 \(p\) 左边的 \(f_j\), 直接 \(f_j \leftarrow f'_j\)

对于 \(p\) 右面的 \(f_j\) 我们有 \(f_j \leftarrow f'_{j - 1} + j \cdot a_i\)

我们可以原先的 \(dp\) 序列中, 在 \(p\) 的位置插入一个值, 为 \(f_{p - 1} + p \cdot a_i\)

注意到从 \(p + 1\) 开始, \(f_{j} - f_{j - 1} = a_i\) 那么我们可以维护一个差分序列, 并且对 \([p + 1, n]\) 进行一个区间加 \(a_i\) .

综上所述, 我们需要一个支持下面操作的数据结构:

  1. 单点插入
  2. 区间加
  3. 单点查询

一般人可能就用 Splay 了, 可我是四班人.

块状链表启动!我们可以用分块的方式保证上面的几个操作都是 \(\mathcal{O}(\sqrt n)\) 的.

块状链表这里不多赘述了.

这样我们就收获了 \(\mathcal{O}(n \sqrt n \log n)\) 的复杂度, 足以通过此题.

#include <bits/stdc++.h>
using namespace std;

namespace OI {

#define int long long
#define endl "\n"

constexpr int maxn = 1e5+5;

int n;
int a[maxn];

// 我是真懒得写了 AI 写的分块
class BlockList {
    private:
    struct Block {
        std::vector<int> data;
        int tag;
        Block* next;
        Block() : tag(0), next(nullptr) {}
    };

    Block* head;
    int total;
    static const int S = 320;
    static const int MAX_SIZE = 2 * S;

    std::pair<Block*, int> find(int pos) const {
        if (pos <= 0 || pos > total) return {nullptr, -1};
        Block* cur = head;
        int idx = pos;
        while (cur) {
            int len = (int)cur->data.size();
            if (idx <= len) {
                return {cur, idx - 1};
            }
            idx -= len;
            cur = cur->next;
        }
        return {nullptr, -1};
    }
    void split(Block* b) {
        if ((int)b->data.size() <= MAX_SIZE) return;
        int mid = b->data.size() / 2;
        Block* nb = new Block();
        nb->data.assign(b->data.begin() + mid, b->data.end());
        b->data.resize(mid);
        nb->tag = b->tag;
        nb->next = b->next;
        b->next = nb;
    }

    public:
    BlockList() : head(nullptr), total(0) {

    }

    ~BlockList() {
        Block* cur = head;
        while (cur) {
            Block* nxt = cur->next;
            delete cur;
            cur = nxt;
        }
    }
    void insert(int pos, int val) {
        if (pos <= 0) pos = 1;
        if (pos > total + 1) pos = total + 1;

        if (!head) {
            head = new Block();
            head->data.push_back(val);
            total = 1;
            return;
        }
        if (pos == total + 1) {
            Block* cur = head;
            while (cur->next) cur = cur->next;
            cur->data.push_back(val - cur->tag);
            total++;
            split(cur);
            return;
        }
        Block* cur = head;
        int idx = pos;
        while (cur) {
            int len = (int)cur->data.size();
            if (idx <= len) {
                cur->data.insert(cur->data.begin() + (idx - 1), val - cur->tag);
                total++;
                split(cur);
                return;
            }
            idx -= len;
            cur = cur->next;
        }
    }
    void add(int l, int r, int val) {
        if (l > r || !head) return;
        if (l < 1) l = 1;
        if (r > total) r = total;
        if (l > r) return;

        auto [bl, ol] = find(l);
        auto [br, or_] = find(r);
        assert(bl && br);

        if (bl == br) {
            for (int i = ol; i <= or_; ++i) {
                bl->data[i] += val;
            }
            return;
        }
        for (int i = ol; i < (int)bl->data.size(); ++i) {
            bl->data[i] += val;
        }
        Block* cur = bl->next;
        while (cur != br) {
            cur->tag += val;
            cur = cur->next;
        }
        for (int i = 0; i <= or_; ++i) {
            br->data[i] += val;
        }
    }
    int query(int x) const {
        if (x == 0) return 0;
        if (x < 1 || x > total) return 0;

        auto [b, off] = find(x);
        if (!b) return 0;
        return b->data[off] + b->tag;
    }
    int size() const { return total; }
} diff;

void main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= n; i++) {
        // for (int j = i; j >= 1; j--)
        //     f[j] = max(f[j], f[j - 1] + j * a[i]);
        
        // f[1...j - 1] 里找到一个 f[p] < f[p - 1] + p * a[i] 且单调
        int l = 1, r = i - 1;
        int p = i;
        while (l <= r) {
            int mid = l + r >> 1;
            if (diff.query(mid) < mid * a[i])
                p = mid,
                r = mid - 1;
            else
                l = mid + 1;
        }
        diff.insert(p, p * a[i]);
        diff.add(p + 1, i, a[i]);
    }
    int sum = 0;
    for (int i = 1; i <= n; i++)
        cout << (sum += diff.query(i)) << ' ';
    cout << endl;
}

#undef int
#undef endl

}

int main() {
    freopen("finale.in", "r", stdin);
    freopen("finale.out", "w", stdout);

    cin.tie(0), cout.tie(0);
    ios::sync_with_stdio(0);
    
    OI::main();

    return 0;
}

写完这题我力竭了.

榜单

方源

不过是些许风霜罢了.

晚饭

吃的是 KFC.

老师(小升)我们吃剩了点你要不要吃

我当时在想啥.

然后找到了 原题 链接, 在核桃上.

T1 T2 T4 原.

HTOJ

并且发现了 CF 的原题(CF573E).

n^2

什么叫你 \(10^5\)\(\mathcal{O}(n^2)\) 过了?

zyq 找到了他昨天丢失的房卡! 强强!!

买了罐白色魔爪, 在这时候没敢喝.

神了, 为什么有人在玩黄游, 还特么在看 CG!

晚自习

神了

神了

我已经力竭了.

写了写总结(其实是日记的子集哈).

然后就一直在写 CBG .

右边有个哥们成功让 DS 开始骂人了.

晚上

飞行棋三连胜!

我听到了「强运」的回响!

然后神秘三国杀, 张绣没打过笮融.

还是没敢喝魔爪.

zyq 还在学 Splay .

我炸框就炸框吧, 马岱 \(5\) 轮没杀还判红桃是啥啊.

还得是势钟会大人!

钟会

行了我要开始熬夜喝魔爪了.

还挺好喝.

没绷住

到现在还剩下一堆 KFC 没吃.

我真没招了.

返回目录

posted @ 2026-07-31 00:09  Kibrel  阅读(6)  评论(0)    收藏  举报