I inner World

I inner World

题目大意

初始给定一排n个树,都只有根节点1,接下来有m次操作,每次给[l,r]的树中的u下子树增加一个节点v。然后是q次询问,每次询问[l,r]的树中u的子树大小之和。请注意,m次操作中每次添加的点都不同。

分析

我们首先关注黒题部分。我们自然可以想到,将m次操作看成是建成了一颗m+1大小的树。树中的每个节点维护一个区间[l,r]

则我们的每次询问即变为了,求对于u而言,其下的子树中对每个节点的维护的区间与询问的区间[l,r]的交集之和。

我们自然而然的想法是,我们直接dfs,我们从子节点回溯时,将该所对应的区间[l,r]在线段树中+1。

然后,我们解决当前节点上的所有询问,假设询问是[L,R],则答案即为query(L,R)即可。

但是,我们发现这样是不行的,因为对于一个父亲的同级儿子,其对线段树的影响应当是互不干涉的。

则我们就需要考虑能否消除其影响。

下面给两种方式消除影响。

直接删除

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

我们考虑分治的方式实现。我们按照dfs进行分治。

我们每次递归到一个区间后,先考虑全部位于左边界的所有子树,再考虑全部位于右边界的所有子树。

接着,我们用双指针从中间开始扫描,开始考虑左右边界分列两边的子树。

最后回溯的时候清空。

这个方法的话,如果修改用线段树会有T掉的问题,使用树状数组则没有。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

#define rep(i, a, b) for (int i(a); i <= b; ++ i ) 
#define dec(i, a, b) for (int i(b); i >= a; -- i ) 

#ifdef LOCAL
  #include <debugger>
  clock_t start = clock();
#else
  #define debug(...) 42
#endif

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }

constexpr int N = 3E5 + 10;

vector<int> son[N];
vector<array<int, 3> > query[N];
int Left[N], Right[N];
int n, m, q, cur; 
int dfn[N], sz[N], idx[N];
ll ans[N];

struct segment_tree {
  ll tr1[N], tr2[N];
  int lowbit(int x) {
    return x & -x;
  }

  void add(ll tr[], int x, ll c) {
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
  }

  void update(int p, int l, int r, int d) {
    add(tr1, l, d);
    add(tr1, r + 1, -d);
    add(tr2, l, d * 1LL * l);
    add(tr2, r + 1, 1LL * (r + 1) * -d);
  }

  ll sum(ll tr[], int x) {
    ll res = 0 ;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
  }

  ll ans(int x) {
    return sum(tr1, x) * (x + 1) - sum(tr2, x);
  }
  
  ll query(int p, int l, int r) {
    return ans(r) - ans(l - 1);
  }

     
}T; 


void dfs1(int u) {
  dfn[u] = ++ cur; sz[u] = 1; idx[dfn[u]] = u; 
  for (int &v: son[u]) {
    dfs1(v);
    sz[u] += sz[v];
  }
}

void dfs (int l, int r) {
  if (l == r) {
    if (sz[idx[l]] != 1) return ;
    T.update(1, Left[idx[l]], Right[idx[l]], 1);
    for (auto [L, R, id] : query[idx[l]]) ans[id] = T.query(1, L, R);
    T.update(1, Left[idx[l]], Right[idx[l]], -1);
    return ;
  }
  int mid = (l + r) / 2;
  dfs (l, mid); dfs(mid + 1, r);
  int p = mid;
  for (int i = mid, j; i >= l && i + sz[idx[i]] - 1 <= r; -- i) {
    j = i + sz[idx[i]] - 1;
    T.update(1, Left[idx[i]], Right[idx[i]], 1);
    if (j <= mid) continue;
    while (p < j) ++ p, T.update(1, Left[idx[p]], Right[idx[p]], 1);
    for (auto [L, R, id] : query[idx[i]]) {
      ans[id] = T.query(1, L, R);
    }
  }

  p = mid;
  for (int i = mid, j; i >= l && i + sz[idx[i]] - 1 <= r; -- i) {
    j = i + sz[idx[i]] - 1;
    T.update(1, Left[idx[i]], Right[idx[i]], -1);
    if (j <= mid) continue;
    while (p < j) ++p, T.update(1, Left[idx[p]], Right[idx[p]], -1);
  }


}

void solve() {
  cin >> n >> m;
  
  for (int i = 1; i <= m; i ++ ) {
    int u, v; cin >> u >> v;
    int x, y; cin >> x >> y;
    son[u].emplace_back(v);
    Left[v] = x, Right[v] = y;
  }
  Left[1] = 1, Right[1] = n;
  dfs1(1);
  cin >> q;

  for (int i = 0; i < q; i ++ ) {
    int x, L, R; cin >> x >> L >> R;
    query[x].push_back({L, R, i});
  }

  dfs(1, m + 1);

  for (int i = 0; i < q; i ++ ) {
    cout << ans[i] << "\n";
  }

}
int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  solve();
#ifdef LOCAL
  clock_t ends = clock();
  // cout << "\n\nRunning Time : " << (double) (ends - start) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
#endif
  return 0;
}

记录进来时的现场来消除影响

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

我们考虑当我们要进入一颗子树的时候,我们可以先把当前子树的根节点,所有要询问的区间的左右端点的

值直接保存下来。

接下来,扫过该子树之后,我们再把当前子树的根节点,所有要询问的区间的左右端点的值查询一下并减去进去时的初值就得到当前子树的结果。

#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
using ll = long long;
const int N = 3e5 + 10;
struct Query
{
    int l,r,id;
};

int n,m,q;
int h[N],e[N],ne[N],idx;
pair<int,int> val[N];
vector<Query> Query[N];
ll ans[N];

void add(int a,int b)
{
    e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}

ll tr1[N], tr2[N];
int lowbit(int x) {
    return x & -x;
}

void add(ll tr[], int x, ll c) {
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

void modify(int p, int l, int r) {
    int d = 1;
    add(tr1, l, d);
    add(tr1, r + 1, -d);
    add(tr2, l, d * 1LL * l);
    add(tr2, r + 1, 1LL * (r + 1) * -d);
}

ll sum(ll tr[], int x) {
    ll res = 0 ;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

ll ANS(int x) {
    return sum(tr1, x) * (x + 1) - sum(tr2, x);
}

ll query(int p, int l, int r) {
    return ANS(r) - ANS(l - 1);
}

void dfs(int u,int fa)
{
    for(int i=h[u];~i;i=ne[i])
    {
        int j = e[i];
        vector<ll> res;
        for(auto x:Query[j])
            res.push_back(query(1,x.l,x.r));
        dfs(j,u);
        for(int k=0;k<Query[j].size();k++)
            ans[Query[j][k].id] = query(1,Query[j][k].l,Query[j][k].r) - res[k];
    }
    modify(1,val[u].first,val[u].second);
}

int main()
{
    ios;
    cin>>n>>m;
    memset(h,-1,sizeof h); 
    val[1] = {1,n};
    for(int i=1;i<=m;i++)
    {
        int u,v,l,r;cin>>u>>v>>l>>r;
        add(u,v);val[v] = {l,r};
    }
    cin>>q;
    for(int i=1;i<=q;i++)
    {
        int x,l,r;
        cin>>x>>l>>r;
        Query[x].push_back({l,r,i});
    }
    vector<ll> res;
    for(auto x:Query[1]) res.push_back(query(1,x.l,x.r));
    dfs(1,-1);
    for(int k=0;k<Query[1].size();k++)
        ans[Query[1][k].id] = query(1,Query[1][k].l,Query[1][k].r) - res[k];
    for(int i=1;i<=q;i++) cout<<ans[i]<<'\n';
    return 0;
}
posted @ 2022-10-08 17:41  艾特玖  阅读(25)  评论(0)    收藏  举报