[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\),有:

  1. 如果 \(a_z < a_x\),则 \(l \in (a_z,a_x], \ r \ge a_y\) 的区间不满足要求。
  2. 如果 \(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;
}

posted @ 2024-02-21 18:40  Saka_Noa  阅读(66)  评论(0)    收藏  举报