DMY 周作业 46 简要题解

D

考虑枚举两个区间最终的 \(\text{MEX}\),发现固定 \(\text{MEX} = x\) 时,所有 \(< x\) 的数都要被包含,所有 \(=x\) 的数都不能被包含。合法区间 \((l, r)\) 相当于一个二维平面上的矩形,求其面积即可。

时间复杂度 \(O(n)\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 200005, inf = 0x3f3f3f3f;
int n, a[N], b[N], posa[N], posb[N];
ll ans;
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        posa[a[i]] = i;
    }
    for(int i = 1; i <= n; i++)
    {
        cin >> b[i];
        posb[b[i]] = i;
    }
    int prela = inf, prera = -inf, prelb = inf, prerb = -inf;
    int tmplen = max(posa[1], posb[1]) - min(posa[1], posb[1]) - 1;
    ans = 1 + 1ll * tmplen * (tmplen + 1) / 2;
    tmplen = min(posa[1], posb[1]) - 1;
    ans += 1ll * tmplen * (tmplen + 1) / 2;
    tmplen = n - max(posa[1], posb[1]);
    ans += 1ll * tmplen * (tmplen + 1) / 2;    
    prela = min(prela, posa[1]);
    prera = max(prera, posa[1]);
    prelb = min(prelb, posb[1]);
    prerb = max(prerb, posb[1]);    
    for(int i = 2; i <= n; i++)
    {
        int la = prela, lb = prelb, ra = prera, rb = prerb;
        int lx = min(prela, prelb);
        int rx = max(prera, prerb);
        if(!(lx <= posa[i] && posa[i] <= rx) && !(lx <= posb[i] && posb[i] <= rx))
        {
            int lt = 1, rt = n;
            if(posa[i] < lx) lt = max(lt, posa[i] + 1);
            else rt = min(rt, posa[i] - 1);
            if(posb[i] < lx) lt = max(lt, posb[i] + 1);
            else rt = min(rt, posb[i] - 1);
            ans += 1ll * (lx - lt + 1) * (rt - rx + 1);
        }
        prela = min(prela, posa[i]);
        prera = max(prera, posa[i]);
        prelb = min(prelb, posb[i]);
        prerb = max(prerb, posb[i]);    
    }
    cout << ans;
    return 0;
}

I

Sol.1

没有利用任何 \(\varphi\) 的性质。先线性筛求出 \(\varphi\) 的表,建树,然后容易发现操作 \(2\) 等价于求 \(\sum_{u =l}^r dep_u - dep_{\text{LCA}}\)

维护 \(\sum dep\) 是容易的。只需要考虑如何求 \(dep_{\text{LCA}}\) 即可。

根据树上查询的经典结论,我们求任意相邻两个节点的 \(\text{LCA}\),区间内任意两个相邻节点 \(\text{LCA}\) 的深度的最小值即为整个区间 \(\text{LCA}\) 的深度。

向上跳节点分为两部分,第一部分是两个都没有跳到 \(\text{LCA}\) 处,\(\text{LCA}\) 不变。第二部分是一个跳到了 \(\text{LCA}\) 处,此时深度更浅的那个节点就是 \(\text{LCA}\)

可能要用吉司机维护 \(\sum dep\),因为要始终保证 \(dep\ge 1\)。时间复杂度 \(O(n\log^2n)\)

Sol.2

延续 Sol.1 的做法。但是不用树上查询的结论。

结论 \(1\):一个点集的 \(\text{LCA}\),为 \(dfn\) 最小的点,与 \(dfn\) 最大的点的 \(\text{LCA}\)

可能还是要吉司机。时间复杂度 \(O(n\log^2n)\)

Sol.3

结论 \(2\):值为 \(V\) 的数,每次操作后变为 \(\varphi(V)\),最多执行 \(O(\log V)\) 次操作就能回到 \(1\)

于是直接势能线段树维护即可。

时间复杂度 \(O(n\log n\log V)\)

#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc (p << 1)
#define rc ((p << 1) | 1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 100005, V = 5000005;
int n, q, cnt, a[N], prm[V], phi[V], b[N];
bitset<V> vis;
struct Edge{
    int v, ne;
} e[V];
int h[V], idx;
void add(int u, int v)
{
    e[++idx] = {v, h[u]};
    h[u] = idx;
}
void seive()
{
    vis[0] = vis[1] = phi[1] = 1;
    for(int i = 2; i < V; i++)
    {
        if(!vis[i])
        {
            prm[++cnt] = i;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= cnt && i * prm[j] < V; j++)
        {
            int v = i * prm[j];
            vis[v] = 1;
            if(i % prm[j]) phi[v] = (prm[j] - 1) * phi[i];
            else
            {
                phi[v] = prm[j] * phi[i];
                break;
            }
        }
        add(phi[i], i);
    }
}
int fa[V], dep[V], top[V], son[V], sz[V];
void dfs1(int u, int father)
{
    fa[u] = father; dep[u] = dep[fa[u]] + 1; sz[u] = 1;
    for(int i = h[u]; i ; i = e[i].ne)
    {
        int v = e[i].v;
        if(v == fa[u]) continue;
        dfs1(v, u);
        sz[u] += sz[v];
        if(sz[son[u]] < sz[v]) son[u] = v;
    }
}
void dfs2(int u, int tp)
{
    top[u] = tp; 
    if(!son[u]) return;
    dfs2(son[u], tp);
    for(int i = h[u]; i ; i = e[i].ne)
    {
        int v = e[i].v;
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}
int getlca(int u, int v)
{
    while(top[u] != top[v])
    {
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        u = fa[top[u]];
    }
    if(dep[u] < dep[v]) swap(u, v);
    return v;
}
struct Node{
    ll l, r, mnv, mndep, mxdep, smdep;
};
struct Segtree{
    Node tr[4 * N];
    void pushup(Node &p, Node ls, Node rs)
    {
        p.mnv = min(ls.mnv, rs.mnv);
        p.mndep = min(ls.mndep, rs.mndep);
        p.mxdep = max(ls.mxdep, rs.mxdep);
        p.smdep = ls.smdep + rs.smdep;
    }
    void build(int p, int ln, int rn)
    {
        tr[p] = {ln, rn, b[ln], dep[a[ln]], dep[a[ln]], dep[a[ln]]};
        if(ln == rn) return;
        int mid = (ln + rn) >> 1;
        build(lc, ln, mid);
        build(rc, mid + 1, rn);
        pushup(tr[p], tr[lc], tr[rc]);
    }
    void update(int p, int ln, int rn)
    {
        if(tr[p].mxdep == 1) return;
        if(tr[p].l == tr[p].r)
        {
            tr[p].mndep--;
            tr[p].mxdep--;
            tr[p].smdep--;
            return;
        }
        int mid = (tr[p].l + tr[p].r) >> 1;
        if(ln <= mid) update(lc, ln, rn);
        if(rn >= mid + 1) update(rc, ln, rn);
        pushup(tr[p], tr[lc], tr[rc]);
    }
    Node query(int p, int ln, int rn)
    {
        if(ln <= tr[p].l && tr[p].r <= rn) return tr[p];
        int mid = (tr[p].l + tr[p].r) >> 1;
        if(rn <= mid) return query(lc, ln, rn);
        if(ln >= mid + 1) return query(rc, ln, rn);
        Node tmp;
        pushup(tmp, query(lc, ln, rn), query(rc, ln, rn));
        return tmp;
    }
} tr1;
int main()
{
    //freopen("sample.in", "r", stdin);
    //freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    seive();
    dfs1(1, 0);
    dfs2(1, 1);
    cin >> n >> q;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        if(i > 1) b[i - 1] = dep[getlca(a[i], a[i - 1])];
    }
    tr1.build(1, 1, n);
    while(q--)
    {
        int opt, l, r;
        cin >> opt >> l >> r;
        if(opt == 1)
            tr1.update(1, l, r);
        else
        {
            Node res = tr1.query(1, l, r);
            ll smdep = res.smdep, mndep = res.mndep;
            if(l <= r - 1) mndep = min(mndep, tr1.query(1, l, r - 1).mnv);
            cout << smdep - mndep * (r - l + 1) << "\n";
        }
    }
    return 0;
}
posted @ 2025-12-04 21:23  KS_Fszha  阅读(4)  评论(0)    收藏  举报