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;
}

浙公网安备 33010602011771号