[线段树 瞎搞] BZOJ 4876 [Zjoi2017]线段树

这个题在考场上应该是能搞出来的 可是当时debuff太强 没敢想正解 写完了250行的暴力
正解呢 就是类似zkw线段树 我们从叶节点 l1 r+1 一直走直到碰在一起 这样搞出了一条左链和一条右链 左链上的右兄弟和右链上的左兄弟就是所有要找的点 然后我们讨论下查询点 u 和链底的lca的位置 进而可以知道链上所有点和u的lca的深度和 这个细节有点多 不对拍很容易写挂 给考场上写挂的人点蜡

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=400005;

int n;
int ls[N],rs[N];

const int K=19;

int depth[N],fat[N][K];
int lcnt[N],rcnt[N]; ll lsum[N],rsum[N];
int idx[N];
int lp[N],rp[N];

inline void dfs(int &u,int fa,int l,int r,bool left){
  u=++n; lp[n]=l; rp[n]=r;
  fat[u][0]=fa; depth[u]=depth[fa]+1;
  for (int k=1;k<K;k++) fat[u][k]=fat[fat[u][k-1]][k-1];
  lcnt[u]=lcnt[fa]+left,lsum[u]=lsum[fa]+left*depth[u];
  rcnt[u]=rcnt[fa]+(!left),rsum[u]=rsum[fa]+(!left)*depth[u];
  if (l==r) return void(idx[l]=n);
  int mid; read(mid);
  dfs(ls[u],u,l,mid,1);
  dfs(rs[u],u,mid+1,r,0);
}

inline int LCA(int u,int v){
  if (depth[u]<depth[v]) swap(u,v);
  for (int k=K-1;~k;k--)
    if ((depth[u]-depth[v])>>k&1)
      u=fat[u][k];
  if (u==v) return u;
  for (int k=K-1;~k;k--)
    if (fat[u][k]!=fat[v][k])
      u=fat[u][k],v=fat[v][k];
  return fat[u][0];
}

int _n;

ll ans=0;
inline ll Lpath(int a,int b,int u,int p){
  ans+=lsum[a]-lsum[b];
  ans+=(ll)(lcnt[a]-lcnt[b])*depth[u];
  ll ret=0;
  int lca=LCA(u,a),t=lca;
  if (depth[lca]<depth[b]) t=b; else if (rs[lca] && lp[rs[lca]]>p) ret+=depth[LCA(rs[lca],u)]-depth[lca];
  ret+=(ll)(lcnt[a]-lcnt[t])*depth[lca];
  ret+=(lsum[t]-lsum[b])-(lcnt[t]-lcnt[b]);
  ans-=2*ret;
}
inline ll Rpath(int a,int b,int u,int p){
  ans+=rsum[a]-rsum[b];
  ans+=(ll)(rcnt[a]-rcnt[b])*depth[u];
  ll ret=0;
  int lca=LCA(u,a),t=lca;
  if (depth[lca]<depth[b]) t=b; else if (ls[lca] && rp[ls[lca]]<p) ret+=depth[LCA(ls[lca],u)]-depth[lca];
  ret+=(ll)(rcnt[a]-rcnt[t])*depth[lca];
  ret+=(rsum[t]-rsum[b])-(rcnt[t]-rcnt[b]);
  ans-=ret*2;
}

int main(){
  int Q,rt,u,l,r;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(_n); dfs(rt,0,1,_n,0);
  read(Q);
  while (Q--){
    read(u); read(l); read(r);
    if (l==1 && r==_n) { printf("%d\n",depth[u]-1); continue; }
    ans=0;
    if (l==1){
      Rpath(idx[r+1],1,u,r+1);
    }else if (r==_n){
      Lpath(idx[l-1],1,u,l-1);
    }else{
      int lca=LCA(idx[l-1],idx[r+1]);
      Lpath(idx[l-1],ls[lca],u,l-1);
      Rpath(idx[r+1],rs[lca],u,r+1);
    }
    printf("%lld\n",ans);
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值