[Ynoi2012]D1T3

https://www.luogu.org/problemnew/show/P5311

题解

先把点分树建出来。

对于吗,每一个询问\((l,r,x)\),我们对于x要找到它在点分树上最靠上的父亲节点使得两点之间的点在\(l \sim r\)中。

然后问题就变成了从一个根出发,可以经过\(l \sim r\)的点,能访问的颜色个数。

那么因为有了点分树,所以我们对于每个点\(dfs\)它的子树的复杂度是对的。

然后对于每个根,我们求出它的子树里的所有点的到根的路径上的编号最大最小值,然后考虑这个点产生贡献的条件,发现就是个二维偏序。

但是还有相同颜色算两边的情况,这个在扫描线的时候用单调性去重就好了,具体来说对于\(r\)都在合法范围内的点,\(l\)更大越优,所以我们只需要保留最大的\(l\)就好了。

代码

#include<bits/stdc++.h>
#define N 100009
#define mm make_pair
using namespace std;
typedef long long ll;
vector<int>fa[N];
vector<pair<int,int> >v[N];
bool vis[N];
int tot,head[N],dp[N],ans[N],size[N],root,tong[N],a[N],n,m,sum;
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
  return f?-x:x;
}
struct edge{
  int n,to;
}e[N<<1];
struct BIT{
  int tr[N];
  inline void add(int x,int y){while(x)tr[x]+=y,x-=x&-x;}
  inline int query(int x){int ans=0;while(x<=n)ans+=tr[x],x+=x&-x;return ans;}
}T;
struct node{
  int id,l,r,opt;
  inline bool operator <(const node &b)const{
    if(r!=b.r)return r<b.r;
    return opt<b.opt;  
  }
};
vector<node>b[N];
vector<node>::iterator it;
inline void add(int u,int v){
  e[++tot].n=head[u];e[tot].to=v;head[u]=tot;
}
void getroot(int u,int fa){
  dp[u]=0;size[u]=1;
  for(int i=head[u];i;i=e[i].n)if(!vis[e[i].to]&&e[i].to!=fa){
      int v=e[i].to;
      getroot(v,u);
      size[u]+=size[v];
      dp[u]=max(dp[u],size[v]);
  }
  dp[u]=max(dp[u],sum-size[u]);
  if(dp[u]<dp[root])root=u;
}
void getsize(int u,int fa){
  size[u]=1;
  for(int i=head[u];i;i=e[i].n)if(!vis[e[i].to]&&e[i].to!=fa){
    int v=e[i].to;
    getsize(v,u);
    size[u]+=size[v];
  }
}
void work(int u,int ff,int ma,int mi,int top){
  ma=max(ma,u);mi=min(mi,u);
  fa[u].push_back(top);
  v[u].push_back(mm(mi,ma));
  for(int i=head[u];i;i=e[i].n)if(e[i].to!=ff&&!vis[e[i].to]){
    int v=e[i].to;
    work(v,u,ma,mi,top);
  }
}
void getcalc(int u,int fa,int mi,int ma){
  mi=min(mi,u);ma=max(ma,u);
  b[root].push_back(node{a[u],mi,ma,0});
  for(int i=head[u];i;i=e[i].n)if(!vis[e[i].to]&&e[i].to!=fa){
    int v=e[i].to;
    getcalc(v,u,mi,ma);
  }
}
void solve(int u){
  vis[u]=1;
  getcalc(u,0,u,u);
  v[u].push_back(mm(u,u));fa[u].push_back(u);
  for(int i=head[u];i;i=e[i].n)if(!vis[e[i].to]){
    int v=e[i].to;
    root=n+1;sum=size[v];
    getroot(v,u);getsize(root,0);
    work(v,0,u,u,u);
    solve(root);
  }
}
int main(){
  n=rd();m=rd();
  for(int i=1;i<=n;++i)a[i]=rd();
  int x,y;
  for(int i=1;i<n;++i){
    x=rd();y=rd();
    add(x,y);add(y,x);
  }
  root=n+1;sum=n;dp[root]=n+1;
  getroot(1,0);getsize(root,0);
  solve(root);
  int l,r;
  for(int i=1;i<=m;++i){
    l=rd();r=rd();x=rd();
    for(int j=0;j<fa[x].size();++j){
      int y=fa[x][j],ls=v[x][j].first,rs=v[x][j].second;
      if(ls>=l&&rs<=r){x=y;break;}
    }
    b[x].push_back(node{i,l,r,1});
  }
  for(int i=1;i<=n;++i){
    sort(b[i].begin(),b[i].end());
    for(it=b[i].begin();it!=b[i].end();++it){
        if(!it->opt){
          if(tong[it->id]<it->l){
            if(tong[it->id])T.add(tong[it->id],-1);
            T.add(it->l,1);
            tong[it->id]=it->l;
          }
        }
        else{
          ans[it->id]=T.query(it->l);
        }
    }
    for(it=b[i].begin();it!=b[i].end();++it)
      if(!it->opt){
        if(tong[it->id])T.add(tong[it->id],-1),tong[it->id]=0;
      }
    b[i].clear();
  }
  for(int i=1;i<=m;++i)printf("%d\n",ans[i]);
  return 0;
}
posted @ 2019-05-28 07:25  comld  阅读(300)  评论(0编辑  收藏  举报