[Ynoi2003] 铃原露露
\(\text{前言}\) :
本篇题解是我对其他题解的理解和梳理。
\(\text{题意}\) :
给你一棵树,和一个 \(n\) 的排列 \(a\)。定义一个满足要对的点对 \((L,R)\) 为:对于 \(\forall x,y\) 如果满足 \(a_x,a_y \in [L,R]\),则 \(a_{Lca(x,y)} \in [L,R]\)。
\(q\) 次询问形如 \(l,r\),求:\([l,r]\) 中满足要求的点对数量。
数据规模:\(n ,q \le 2\times 10 ^5\)
\(\text{分析}\) :
我们知道,\(Lca\) 满足相关性质我们常用点分治或者 dsu on tree,与距离相关常用点分治,这题我们尝试使用 dsu on tree 做。
令 \(Lca(x,y) = z\),我们考虑一个合法的区间满足 \(a_x < a_z < a_y\)。我们考虑什么样的区间不满足要求。
不妨设 \(a_x < a_y\),有:
- 如果 \(a_z < a_x\),则 \(l \in (a_z,a_x], \ r \ge a_y\) 的区间不满足要求。
- 如果 \(a_z > a_y\),则 \(l \in [1,a_x], \ r \in [a_y,a_z)\) 的区间不满足要求。
但是枚举这样的点对是 \(O(n^2)\) 的。观察发现,对于同一个 \(z\),不合法点对 \((x,y)\),\((d,e)\) 如果都不满足限制 \(1\),如果 \([x,y] \subset [d,e]\),显然 \([x,y]\) 可以约束到更多的点,那么另一个点对就可以删去。\(2\) 同理。
这样我们就可以在 dsu on tree 中对于每个点 \(x\) 找 \(a_x\) 的前驱后继,这样我们可以找出大概 \(2 \times n \log n\) 个点对。
发现我们大概要求一个矩形内符合要求的点,
我们把询问离线下来,用扫描线做。枚举每个右端点,累计左端点可能的个数。大概来说就是求历史最小值 \(0\) 的个数和,这是满足区间可减性的,即可以差分。如果不满足的话,我们通常可以分治求解,不过要多带一只 \(\log\)。复杂度 $O (n \log^2 n) $。
\(\text{Add} \ \ 1.20.2024\)
我当时写这篇题解的时候还没有敲代码,后来我自己写的时候发现维护历史 \(0\) 的个数之和并不是很好做,就顺便提一嘴。
首先我们要求历史 \(0\) 的个数和,也就是各个版本 \(0\) 的个数之和。因此我们考虑维护当前最小值,当前最小值个数,历史 \(0\) 个数之和。
首先有一个想法是在 \(\operatorname{push\ up}\) 的时候记录时间差来维护一段时间的贡献,但是这样不好处理 \(\operatorname{push\ down}\)。
之后我 \(\text{Search Online}\),但是没找到。然后参考其他了题解,发现了一个很聪明(可能是套路)的做法:记一个 \(\operatorname{tag_2}\) 把时间差累积起来算贡献。即在扫描线每次移动 \(r\) 的时候都在整棵树为 \(0\) 的区间打上一个 \(\operatorname{tag_2}\)。但是我不太会分析这个的复杂度,等我再研究一下。
还有个小细节,我们打 \(\operatorname{tag_2}\) 的时候可以只在从 \(1\) 到当前枚举的右端点 \(r\) 的区间打标记,这样我们不用查询两次做差分,少一半的查询。
因为我写完了,给出参考代码。
#include<bits/stdc++.h>
#define pb emplace_back
#define pi pair<int,int>
#define mp make_pair
#define fi first
#define se second
const int N=2e5+50;
typedef long long ll;
using namespace std;
int n,m;
int a[N],sei[N], so[N];
vector<int>e[N];
set<int>sg[N];
vector<pi> Re[N];
bool cmp(int a,int b){return sg[sei[a]].size() > sg[sei[b]].size();}
void dsu(int u){
int sot=0;
for(auto v : e[u]) dsu(v);
for(auto v : e[u]) so[++sot] = v;
if(!sot) {sg[sei[u]].insert(a[u]);return;}
sort(so+1,so+sot+1,cmp);
int fu = so[1];
for(int i=2;i<=sot;++i){
int nv = so[i];
for(auto v : sg[sei[nv]]){
auto ql = sg[sei[fu]].lower_bound(v);
if(ql!=sg[sei[fu]].end()){
int x = v, y = *ql, z = a[u];
if(x > y) swap(x, y);
if(z < x) {Re[y].pb(mp(z + 1, x));}
if(z > y) {Re[y].pb(mp(1, x));Re[z].pb(mp(-1, x));}
}
if(ql!=sg[sei[fu]].begin()) {
ql--;
int x = v, y = *ql, z = a[u];
if(x > y) swap(x, y);
if(z < x) {Re[y].pb(mp(z + 1, x));}
if(z > y) {Re[y].pb(mp(1, x));Re[z].pb(mp(-1, x));}
}
}
for(auto v : sg[sei[nv]]) sg[sei[fu]].insert(v);
}
sei[u]=sei[fu];
sg[sei[fu]].insert(a[u]);
}
struct node{
ll tsm, nsm;
int tmin;
node operator+(const node b) const {
if(!nsm && !tsm) return b;
if(!b.nsm && !b.tsm) return *this;
node ans;
ans.tsm = tsm + b.tsm;
ans.tmin = min(tmin, b.tmin);
ans.nsm = nsm * (tmin == ans.tmin) + b.nsm * (ans.tmin == b.tmin);
return ans;
}
};
struct segment{
#define lc k << 1
#define rc k << 1 | 1
#define lcon lc,l,mid
#define rcon rc,mid+1,r
#define Mid int mid=l+r>>1
node tm[N << 2];
int tag1[N << 2], tag2[N << 2];
void pu(int k){
tm[k] = tm[lc] + tm[rc];
}
void ad1(int k,int v){
tag1[k] += v;
tm[k].tmin += v;
}
void ad2(int k,int v){
tag2[k] += v;
tm[k].tsm += (ll)tm[k].nsm * v;
}
void build(int k,int l,int r){
tm[k].nsm = r - l + 1;
if(l==r)return; Mid;
build(lcon), build(rcon); pu(k);
}
void pd(int k){
if(tag1[k]){
ad1(lc,tag1[k]);
ad1(rc,tag1[k]);
}
if(tag2[k]){
if(tm[lc].tmin == tm[k].tmin) ad2(lc,tag2[k]);
if(tm[rc].tmin == tm[k].tmin) ad2(rc,tag2[k]);
}
tag1[k]=tag2[k]=0;
}
void mo(int k,int l,int r,int x,int y,int v){
if(x>y)return;if(x<=l&&r<=y) return ad1(k,v);
Mid; pd(k); if(x<=mid) mo(lcon,x,y,v);
if(y>mid) mo(rcon,x,y,v); pu(k);
}
void update(int k,int l,int r,int x,int y,int v){
if(x>y||tm[k].tmin>0)return;
if(x<=l&&r<=y) return ad2(k,v);
Mid; pd(k); if(x<=mid) update(lcon,x,y,v);
if(y>mid) update(rcon,x,y,v); pu(k);
}
ll qr(int k,int l,int r,int x,int y){
if(x<=l&&r<=y)return tm[k].tsm;
Mid; pd(k); ll ans = 0;
if(x<=mid) ans+=qr(lcon,x,y);
if(y>mid) ans+=qr(rcon,x,y);
return ans;
}
}ti;
vector<pi>rq[N];
ll ans[N];
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=2,x;i<=n;++i)cin>>x,e[x].pb(i);
for(int i=1;i<=n;++i)sei[i]=i;
dsu(1); ti.build(1,1,n);
for(int i=1;i<=m;++i){
int l,r; cin >> l >> r;
rq[r].pb(mp(l,i));
}
for(int i=1;i<=n;++i){
for(auto [l, r] : Re[i]) {
if(l > 0) ti.mo(1,1,n,l,r,1);
else ti.mo(1,1,n,-l,r,-1);
}
ti.update(1,1,n,1,i,1);
for(auto [l, id] : rq[i]) {
ans[id] = ti.qr(1, 1, n, l, i);
}
}
for(int i=1;i<=m;++i) cout << ans[i] << "\n";
return 0;
}

浙公网安备 33010602011771号