【BZOJ】4012: [HNOI2015]开店-点分树/树剖+主席树

传送门:bzoj4012

这题码+调试就花了一中午和一下午。。。最后发现问题都是有地方没开 l o n g   l o n g long \ long long longTAT。。。

法2的代码比较有技巧性(还不太熟。


题解

法1(点分树):

首先点分治处理出 v : v: v:每个重心到其所管辖子树中每个点的距离,压进vector后按年龄排序,转成距离的前缀和, f v : fv: fv:该点点分树中的父亲结点到这颗子树每个点的距离,同样压进vector后排序求前缀和(容斥用)

暴力跳点分树,每层二分找到需要的区间处理即可。细节可以自己再想一下。

法2(树剖+主席树):

注意到 d i s ( u , v ) = d e p ( u ) + d e p ( v ) − 2 × d e p ( L C A ( u , v ) ) dis(u,v)=dep(u)+dep(v)-2\times dep(LCA(u,v)) dis(u,v)=dep(u)+dep(v)2×dep(LCA(u,v)),于是点之间的关系转成了分别处理每个点对于答案的贡献。

某个年龄区间的 d e p ( v ) dep(v) dep(v)可以排序+前缀和处理。关键问题在于如何快速求出 ∑ d e p ( L C A ( u , v ) ) \sum dep(LCA(u,v)) dep(LCA(u,v))

采用一种 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的方法:把每个点到根路径上的所有边覆盖次数+1, ∑ d e p ( L C A ( u , v ) ) \sum dep(LCA(u,v)) dep(LCA(u,v))就是 u u u到根路径上的每条边的边权 × \times ×覆盖次数。

由于每次查询的是一段年龄连续的区间,可以建立主席树,将点按年龄排序后逐个加入。

注意:点数太多,需要标记永久化。


代码

点分树:

#include<bits/stdc++.h>
#define pb push_back
#define mid ((L+R)>>1)
using namespace std;
const int N=15e4+10;
typedef long long ll;

int n,m,A,ag[N];ll ans;
int head[N],to[N<<1],nxt[N<<1],w[N<<1],tot;
int sz[N],f[N],son[N],top[N],dep[N],dis[N];
clock_t stt,edd;

char cp,OS[100];
template<class yyy>inline void rd(yyy &x)
{
    cp=getchar();x=0;
    for(;!isdigit(cp);cp=getchar());
    for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}

template<class yyy>inline void ot(yyy x)
{
    int re=0;for(;(!re)||(x);x/=10) OS[++re]='0'+x%10;
    for(OS[0]='\n';~re;--re) putchar(OS[re]);
}

inline void lk(int u,int v,int vv)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;}

void dfs(int x)
{
    sz[x]=1;
    for(int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(j==f[x]) continue;
        f[j]=x;dep[j]=dep[x]+1;dis[j]=dis[x]+w[i];dfs(j);
        sz[x]+=sz[j];if(sz[j]>sz[son[x]]) son[x]=j; 
    }
}

void dfss(int x,int tpo)
{
    top[x]=tpo;if(!son[x]) return;dfss(son[x],tpo);
    for(int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(j==f[x] || j==son[x]) continue;
        dfss(j,j);
    }
}

inline int LCA(int x,int y){for(;top[x]!=top[y];x=f[top[x]]) if(dep[top[x]]<dep[top[y]]) swap(x,y);
return dep[x]<dep[y]?x:y;}
inline int dist(int x,int y){
return dis[x]+dis[y]-(dis[LCA(x,y)]<<1);}

namespace ds{
     int f[N],mx[N],MN,S,rt,vs[N],b;
     struct P{ 
        int yr;ll d;
        P(int yr_=0,ll d_=0):yr(yr_),d(d_){};
        bool operator<(const P&ky)const{return yr<ky.yr;}
        bool operator <=(const P&ky)const{return yr<=ky.yr;}
     };
     vector<P>v[N],fv[N];

     void getrt(int x,int fr)
     {
          sz[x]=1;mx[x]=0;
          for(int j,i=head[x];i;i=nxt[i]){
             j=to[i];if(j==fr || vs[j]) continue;
             getrt(j,x);sz[x]+=sz[j];mx[x]=max(mx[x],sz[j]);
          }
          mx[x]=max(mx[x],S-sz[x]);
          if(mx[x]<MN) {MN=mx[x];rt=x;}
     }

     void dfs(int x,int fr,int ds)
     {
         v[b].pb(P(ag[x],ds));
         for(int j,i=head[x];i;i=nxt[i]){
            j=to[i];if(j==fr || vs[j]) continue;
            dfs(j,x,ds+w[i]);
         }
     }
     
     void dfss(int x,int fr,int ds)
     {
         fv[b].pb(P(ag[x],ds));
         for(int j,i=head[x];i;i=nxt[i]){
            j=to[i];if(j==fr || vs[j]) continue;
            dfss(j,x,ds+w[i]);
         }
     }

     void build(int x)
     {
         int i,j,k,sq=S;vs[x]=1;b=x;dfs(x,0,0);
         sort(v[x].begin(),v[x].end());j=v[x].size();
         for(i=1;i<j;++i) v[x][i].d+=v[x][i-1].d;
         for(int j,i=head[x];i;i=nxt[i]){
            j=to[i];if(vs[j]) continue;S=(sz[j]>sz[x])?(sq-sz[x]):sz[j];MN=n+1;
            getrt(j,x);f[rt]=x;b=rt;dfss(j,x,w[i]);
            sort(fv[rt].begin(),fv[rt].end());j=fv[rt].size();
            for(k=1;k<j;++k) fv[rt][k].d+=fv[rt][k-1].d;build(rt);
         }
     }

     inline ll cala(int l,int r,int dd)
     {
         int x=v[b].size(),y=-1,L=0,R=v[b].size()-1;
         for(;L<=R;) 
           v[b][mid].yr>=l?(R=(x=mid)-1):(L=mid+1);
         for(L=0,R=v[b].size()-1;L<=R;) 
           v[b][mid].yr<=r?(L=(y=mid)+1):(R=mid-1);
         if(x>y) return 0LL;
         return v[b][y].d-(x?v[b][x-1].d:0)+(ll)(y-x+1)*dd;
     }
     
     inline ll calb(int l,int r,int dd)
     {
     	 int x=fv[b].size(),y=-1,L=0,R=fv[b].size()-1;
     	 for(;L<=R;) 
            fv[b][mid].yr>=l?(R=(x=mid)-1):(L=mid+1);
         for(L=0,R=fv[b].size()-1;L<=R;) 
           fv[b][mid].yr<=r?(L=(y=mid)+1):(R=mid-1);
         if(x>y) return 0LL;
     	 return fv[b][y].d-(x?fv[b][x-1].d:0)+(ll)(y-x+1)*dd;
     }

     inline void query(int x,int l,int r)
     {
          b=x;ans=cala(l,r,0);int dd,ori=x;
          for(;f[x];x=b){
              dd=dist(ori,f[x]);
              b=x;ans-=calb(l,r,dd);
              b=f[x];ans+=cala(l,r,dd);
          }
     }
}

int main(){	
    using namespace ds;
    int i,j,k,x,y,z;
    rd(n);rd(m);rd(A);
    for(i=1;i<=n;++i) rd(ag[i]);
    for(i=1;i<n;++i){
        rd(x);rd(y);rd(z);lk(x,y,z);lk(y,x,z);
    }
    dep[1]=1;dfs(1);dfss(1,1);
    MN=n+1;S=n;getrt(1,0);
    build(rt);
    for(;m;--m){
        rd(x);rd(y);rd(z);y=(ans+y)%A;z=(ans+z)%A;
        query(x,min(y,z),max(y,z));ot(ans);
    }
    return 0;
}

树剖+主席树

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
const int N=15e4+10,M=2e7+10;
typedef long long ll;

int n,m,A,vl[N],bl[N],num,df[N],dfn;ll ans;
int head[N],to[N<<1],nxt[N<<1],w[N<<1],tot,dis[N];
int sz[N],f[N],son[N],top[N],dep[N],rt[N],cnt;
int ls[M],rs[M],st[M];ll ss[M],qz[N];

struct P{
    int id,v;
    bool operator<(const P&ky)const{return v<ky.v;}
}q[N];
clock_t stt,edd;

char cp,OS[100];
template<class yyy>inline void rd(yyy &x)
{
    cp=getchar();x=0;
    for(;!isdigit(cp);cp=getchar());
    for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}

template<class yyy>inline void ot(yyy x)
{
    int re=0;for(;(!re)||(x);x/=10) OS[++re]='0'+x%10;
    for(OS[0]='\n';~re;--re) putchar(OS[re]);
}

inline void lk(int u,int v,int vv)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;}

void dfs(int x)
{
    sz[x]=1;
    for(int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(j==f[x]) continue;
        bl[j]=w[i];f[j]=x;dep[j]=dep[x]+1;dis[j]=dis[x]+w[i];dfs(j);
        sz[x]+=sz[j];if(sz[j]>sz[son[x]]) son[x]=j; 
    }
}

void dfss(int x,int tpo)
{
    top[x]=tpo;df[x]=++dfn;vl[dfn]=bl[x];
    if(!son[x]) return;dfss(son[x],tpo);
    for(int j,i=head[x];i;i=nxt[i]){
        j=to[i];if(j==f[x] || j==son[x]) continue;
        dfss(j,j);
    }
}

void ad(int pr,int &k,int l,int r,int L,int R)
{
    if(k==pr){k=++cnt;st[k]=st[pr];ss[k]=ss[pr];ls[k]=ls[pr];rs[k]=rs[pr];}	
    if(L==l && r==R) {st[k]++;return;}ss[k]+=(vl[R]-vl[L-1]);
    if(L<=mid) ad(ls[pr],ls[k],l,mid,L,min(R,mid));
    if(R>mid) ad(rs[pr],rs[k],mid+1,r,max(L,mid+1),R);
}

ll ask(int k,int l,int r,int L,int R)
{
    if(!k) return 0LL;
    if(L==l && r==R) return ss[k]+(ll)st[k]*(vl[r]-vl[l-1]);
    ll re=(ll)(vl[R]-vl[L-1])*st[k];
    if(L<=mid) re+=ask(ls[k],l,mid,L,min(R,mid));
    if(R>mid) re+=ask(rs[k],mid+1,r,max(L,mid+1),R);
    return re;
}

inline void query(int x,int L,int R)
{
    if(L>R) {ans=0LL;return;}ans=qz[R]-qz[L-1]+(ll)dis[x]*(R-L+1);
    L=rt[L-1];R=rt[R];
    for(;x;x=f[top[x]]) 
    ans-=((ask(R,1,n,df[top[x]],df[x])-ask(L,1,n,df[top[x]],df[x]))<<1);
}

inline int dn(int x)
{
    int l=1,r=n,re=n+1;
    for(;l<=r;) q[mid].v>=x?(r=(re=mid)-1):(l=mid+1);
    return re;
}

inline int up(int x)
{
    int l=1,r=n,re=-1;
    for(;l<=r;) q[mid].v<=x?(l=(re=mid)+1):(r=mid-1);
    return re;
}

int main(){	
    int i,j,k,x,y,z;
    rd(n);rd(m);rd(A);vl[0]=0;qz[0]=0LL;
    for(i=1;i<=n;++i) rd(q[i].v),q[i].id=i;sort(q+1,q+n+1);
    for(i=1;i<n;++i){rd(x);rd(y);rd(z);lk(x,y,z);lk(y,x,z);}
    dep[1]=1;dfs(1);dfss(1,1);for(i=1;i<=n;++i) vl[i]+=vl[i-1],qz[i]=qz[i-1]+dis[q[i].id];
    for(i=1;i<=n;++i)
    	for(rt[i]=rt[i-1],x=q[i].id;x;x=f[top[x]]) ad(rt[i-1],rt[i],1,n,df[top[x]],df[x]);
    for(;m;--m){
        rd(x);rd(y);rd(z);y=(ans+y)%A;z=(ans+z)%A;if(y>z) swap(y,z);
        query(x,dn(y),up(z));ot(ans);
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值