Loading

12.10 CW 模拟赛 T3. 循环

算法

很容易想到枚举短边断环之后 \(\mathcal{O} (P)\) 的求答案

那么这个算法还有前途吗?

可以发现, 对于每次枚举断边, 断 \((i, i + 1)\)\((i - 1, i)\) 这两条边, 变化量并不大, 严格来说, 均摊复杂度 \(\mathcal{O} (P)\)

具体实现上怎么处理呢?

将断第 \(x\) 条边作为横轴, 满足每一个点对需要的边放在 \(y\) 轴, 显然的, 这就变成了一些由矩阵组合而成的图形, 注意到可以用扫描线来处理

对于每一条边, 我们预处理出其需要被使用的起始点 / 终结点, 具体的, 对于每一个要求点对 \((a,b)\) , \([1,a),[b,n]\) 中的边不选时其覆盖区间为 \([a,b)\) , 否则为 \([1,a),[b,n]\)

然后, 扫描 \(x\) 轴, 对于 \(y\) 轴, 线段树区间查询

代码

放一个 \(\rm{ZCY}\) 大佬的代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
#define int long long
int n, P, M, S, c[N], l[N], r[N], f[N], cnt;
struct seg
{
    int cnt, sum;
} s[N << 2];
struct scan
{
    int y_up, y_down, x, w;
} a[N << 3];
int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
void pushup(int l, int r, int rt)
{
    if (s[rt].cnt)
        s[rt].sum = f[r] - f[l - 1];
    else
        s[rt].sum = s[rt << 1].sum + s[rt << 1 | 1].sum;
}
void update(int l, int r, int rt, int L, int R, int w)
{
    if (L > R)
        return;
    if (L <= l && R >= r)
    {
        s[rt].cnt += w;
        pushup(l, r, rt);
        return;
    }
    int mid = (l + r) >> 1;
    if (L <= mid)
        update(l, mid, rt << 1, L, R, w);
    if (R > mid)
        update(mid + 1, r, rt << 1 | 1, L, R, w);
    pushup(l, r, rt);
}
int query(int l, int r, int rt, int L, int R)
{
    if (L <= l && R >= r)
        return s[rt].sum;
    int mid = (l + r) >> 1, res = 0;
    if (L <= mid)
        res += query(l, mid, rt << 1, L, R);
    if (R > mid)
        res += query(mid + 1, r, rt << 1 | 1, L, R);
    return res;
}
bool cmp(scan aa, scan bb)
{
    return aa.x < bb.x;
}
signed main()
{
    cin >> n >> P >> M >> S;
    for (int i = 1; i <= n; i++)
    {
        S = (S * 1103515245 + 12345) % (1LL << 31);
        c[i] = 1 + ((S / 10) % M);
        f[i] = f[i - 1] + c[i];
    }
    for (int i = 1; i <= P; i++)
    {
        S = (S * 1103515245 + 12345) % (1LL << 31);
        l[i] = ((S / 10) % n) + 1;
        S = (S * 1103515245 + 12345) % (1LL << 31);
        r[i] = ((S / 10) % n) + 1;
        if (l[i] > r[i])
            swap(l[i], r[i]);
    }
    for (int i = 1; i <= P; i++)
    {
        if (l[i] != 1)
        {
            update(1, n, 1, l[i], r[i] - 1, 1);
            a[++cnt].x = l[i];
            a[cnt].y_down = l[i], a[cnt].y_up = r[i] - 1, a[cnt].w = -1;

            a[++cnt].x = l[i];
            a[cnt].y_down = 1, a[cnt].y_up = l[i] - 1, a[cnt].w = 1;

            a[++cnt].x = r[i];
            a[cnt].y_down = 1, a[cnt].y_up = l[i] - 1, a[cnt].w = -1;
        }
        a[++cnt].x = l[i];
        a[cnt].y_down = r[i], a[cnt].y_up = n, a[cnt].w = 1;

        a[++cnt].x = r[i];
        a[cnt].y_down = r[i], a[cnt].y_up = n, a[cnt].w = -1;

        a[++cnt].x = r[i];
        a[cnt].y_down = l[i], a[cnt].y_up = r[i] - 1, a[cnt].w = 1;
    }
    sort(a + 1, a + cnt + 1, cmp);
    int ans = 1e18;
    a[0].x = 1;
    for (int i = 1; i <= cnt; i++)
    {
        if (a[i].x != a[i - 1].x)
            ans = min(ans, s[1].sum);
        update(1, n, 1, a[i].y_down, a[i].y_up, a[i].w);
    }
    ans = min(ans, s[1].sum);
    cout << ans;
    return 0;
}

总结

善于找到暴力算法为什么劣

扫描线可以处理一些均摊复杂度问题

posted @ 2024-12-11 19:13  Yorg  阅读(4)  评论(0编辑  收藏  举报