1. 程式人生 > >codeforces 893F - Physical Education Lessons 動態開點線段樹合併

codeforces 893F - Physical Education Lessons 動態開點線段樹合併

https://codeforces.com/contest/893/problem/F

題意:

  給一個有根樹,

  多次查詢,每次查詢對於$x$i點的子樹中,距離$x$小於等於$k$的所有點中權值最小的一個

  查詢強制線上

題解:

  顯然,暴力就是,對於每次搜尋深搜距離x小於$k$的所有點搜尋

  那麼我們考慮優化

  首先,查詢對$x$距離小於$k$的所有點,等價於在整顆樹上,查詢$\forall dep(x)≤dep(i)≤dep(x)+k$中,在$x$子樹中的點的最小值

  那麼,一個大膽的想法就是,對於每個點,用深度去維護區間$[1,n]$,區間資訊則為x的子樹中,深度$[l,r]$中節點的最小值

  顯然,每個點如果真的開了一個線段樹,有兩個問題

  1.空間是$O(n^2logn)$

  2.時間是$O(n^2logn)$

  但顯然的,本題的詢問一定程度上,滿足區間加法

  或者說,其父親的資訊為其子樹資訊的"和",

  那麼我們可以用動態開點線段樹+線段樹合併的方式

    而對於任意的合法詢問,動態開點線段樹中,也一定在建樹的過程中被建立過了(兒子被合併到父親中了)

  空間複雜度降低到$O(nlogn^2)$,時間複雜度降低到$O(nlogn)$

  

#include <bits/stdc++.h>
#define endl '\n'
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
using namespace std;
int casn,n,m,k;
const int maxn=1e5+7,maxm=1e7+7;
const ll INF=0x3f3f3f3f3f3f3f;
class graph{public:
  struct edge{
    int from,to;ll cost;
    edge(int a,int b,ll c){from=a,to=b,cost=c;}
  };
  vector<vector<edge>> node;
  int ud=0;
  graph(int n=maxn,int f=0){node.resize(n+2);ud=f;}
  void add(int a,int b,int c=1){node[a].emplace_back(a,b,c);if(ud)node[b].emplace_back(b,a,c);}
};
class dsegtree{public:
#define nd  node[now]
#define ndl node[node[now].son[0]]
#define ndr node[node[now].son[1]]
  struct dsegnode {
    int son[2];ll val;
    dsegnode(){val=INF;}
    dsegnode(int x){son[0]=son[1]=0;}
    void update(ll x){val=x;}
  };
  vector<dsegnode> node;
  vector<int> root;
  int cnt,n,s,t,pos;
  dsegtree(int nn,int size=maxm){
    n=nn,cnt=0;
    node.resize(size);
    root.resize(n+2);
  }
  void pushup(int now){nd.val=min(ndl.val,ndr.val);}
  void pushdown(int now){}
  void change(int p,ll x,int now){
    pos=p;
    if(!root[now]) root[now]=++cnt;
    update(1,n,x,root[now]);
  }
  void update(int l,int r,ll x,int now){
    if(pos>r||pos<l) return ;
    if(l==r){
      nd.update(x);
      return ;
    }
    if(!nd.son[0]) nd.son[0]=++cnt;
    if(!nd.son[1]) nd.son[1]=++cnt;
    pushdown(now);
    update(l,(l+r)>>1,x,nd.son[0]);
    update(((l+r)>>1)+1,r,x,nd.son[1]);
    pushup(now);
  }
  void unite(int a,int b){root[a]=merge(root[a],root[b]);}
  int merge(int a,int b){
    if(!a||!b) return a^b;
    int now=++cnt;
    nd.son[0]=merge(node[a].son[0],node[b].son[0]);
    nd.son[1]=merge(node[a].son[1],node[b].son[1]);
    nd.val=min(node[a].val,node[b].val);
    return now;
  }
  ll query(int ss,int tt,int now){s=ss,t=tt;return count(1,n,root[now]);}
  ll count(int l,int r,int now){
    if(s>r||t<l) return INF;
    if(s<=l&&t>=r) return nd.val;
    return min(count(l,(l+r)>>1,nd.son[0]),count(((l+r)>>1)+1,r,nd.son[1]));
  }
};
int main() {
  IO;
  ll n,m,root;
  cin>>n>>root;
  vector<int> dep(n+7,0);
  vector<ll> val(n+7);
  rep(i,1,n) cin>>val[i];
  graph g(n,1);
  register ll a,b;
  rep(i,2,n){
    cin>>a>>b;
    g.add(a,b);
  }
  dsegtree tree(n);
  auto dfs=[&tree,&g,&val,&dep](auto &&dfs,int now,int pre=-1,int dis=1)->void{
    dep[now]=dis;
    tree.change(dis,val[now],now);
    for(auto &i:g.node[now]){
      if(i.to==pre) continue;
      dfs(dfs,i.to,now,dis+1);
      tree.unite(now,i.to);
    }
  };
  dfs(dfs,root);
  cin>>m;
  ll x=0,last=0,k=0;
  while(m--){
    cin>>a>>b;
    x=(a+last)%n+1,k=(b+last)%n;
    last=tree.query(dep[x],dep[x]+k,x);
    cout<<last<<endl;
  }
  return 0;
}