bzoj4012 [HNOI2015]开店(动态点分治+二分+STL/树链剖分+主席树)

如何求所有点到一个点的距离和呢?类似bzoj3924我们的处理方法。
对于每个节点x维护
s1[x]–x的子树中的点到x的距离
s2[x]–x的子树中的点到fa[x]的距离
然后logn的在重心树上跳一跳,减去重复的,补上少的即可。
这题还有一个年龄的限制,怎么办呢?很容易想到线段树,但是我不会写gg
我们还可以利用vector+二分来做。
把所有点按年龄从小到大排序,做前缀和。每次二分一下就可以得到年龄<=x的所有点的距离和了。复杂度 O(nlog2n) O ( n l o g 2 n ) 不过我是常数起飞了qaq

2.24upd:还有一种树剖+主席树的做法,常数貌似比较优秀。
考虑如果没有颜色的限制,如何求所有点到一个点u的距离和。应该是
ni=1dis[u]+dis[v[i]]2dis[lca(u,v[i])] ∑ i = 1 n d i s [ u ] + d i s [ v [ i ] ] − 2 ∗ d i s [ l c a ( u , v [ i ] ) ]
dis[u]n+sumdis2ni=1dis[lca(u,v[i])] d i s [ u ] ∗ n + s u m d i s − 2 ∑ i = 1 n d i s [ l c a ( u , v [ i ] ) ]
所以我们就只是要求 ni=1dis[lca(u,v[i])] ∑ i = 1 n d i s [ l c a ( u , v [ i ] ) ]
怎么求呢?我们可以对n个点都做一遍路径覆盖(v[i],rt),出现次数+1。
然后求 ni=1dis[lca(u,v[i])] ∑ i = 1 n d i s [ l c a ( u , v [ i ] ) ] 就相当于是查询(u,rt)的边权*出现次数的和。
那么对于颜色限制怎么办呢?我们按年龄顺序依次进行覆盖,并用主席树可持久化。然后每次查询时就是0~r的答案减去0~l-1的答案即可。
tips:主席树不能pushdown!也就不能update!要绝对标记!
这题的空间本来应该开 O(nlog2n) O ( n l o g 2 n ) 的,但是开不下。。。树剖不是常数小吗,我们就少开点。。。不知能有没有数据能卡。

动态点分治+vector+二分

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 150010
#define pa pair<int,ll>
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,m,mod,fa[N],a[N],h[N],num=0,sz[N],f[N],sumsz,rt,dis[N],dep[N];
bool vis[N];int dfn=0,pos[N],mn[N<<1][20],Log[N<<1];ll ans=0;
vector<pa>s1[N],s2[N];
struct edge{
    int to,next,val;
}data[N<<1];
inline void dfs(int x,int Fa){
    mn[++dfn][0]=x;pos[x]=dfn;
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa) continue;
        dis[y]=dis[x]+data[i].val;dep[y]=dep[x]+1;
        dfs(y,x);mn[++dfn][0]=x;
    }
}
inline int Min(int x,int y){return dep[x]<dep[y]?x:y;}
inline void inirmq(){
    Log[0]=-1;
    for(int i=1;i<=n<<1;++i) Log[i]=Log[i>>1]+1;
    for(int i=1;i<=Log[n<<1];++i)
        for(int j=1;j<=n*2-1;++j){
            if(j+(1<<i-1)>n*2-1) break;
            mn[j][i]=Min(mn[j][i-1],mn[j+(1<<i-1)][i-1]);
        }
}
inline void dfs1(int x,int Fa){
    sz[x]=1;
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa||vis[y]) continue;
        dfs1(y,x);sz[x]+=sz[y];
    }
}
inline void dfs2(int x,int Fa){
    f[x]=0;
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa||vis[y]) continue;
        dfs2(y,x);f[x]=max(f[x],sz[y]);
    }f[x]=max(f[x],sumsz-sz[x]);if(f[x]<f[rt]) rt=x;
}
inline void getG(int x){
    vis[x]=1;dfs1(x,0);
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(vis[y]) continue;
        sumsz=sz[y];rt=0;dfs2(y,0);fa[rt]=x;getG(rt);
    }
}
inline int lca(int x,int y){
    x=pos[x];y=pos[y];if(x>y) swap(x,y);
    int t=Log[y-x+1];
    return Min(mn[x][t],mn[y-(1<<t)+1][t]);
}
inline int caldis(int x,int y){
    return dis[x]+dis[y]-dis[lca(x,y)]*2;
}
inline ll calc(int x,int lim){
    ll res=0;int t=0;pa P=make_pair(lim+1,0);
    t=lower_bound(s1[x].begin(),s1[x].end(),P)-s1[x].begin()-1;
    res+=s1[x][t].second;
    for(int i=x;fa[i];i=fa[i]){
        int diss=caldis(x,fa[i]);
        t=lower_bound(s1[fa[i]].begin(),s1[fa[i]].end(),P)-s1[fa[i]].begin()-1;
        res+=s1[fa[i]][t].second+(ll)t*diss;
        t=lower_bound(s2[i].begin(),s2[i].end(),P)-s2[i].begin()-1;
        res-=s2[i][t].second+(ll)t*diss;
    }return res;
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();m=read();mod=read();f[0]=inf;
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<n;++i){
        int x=read(),y=read(),val=read();
        data[++num].to=y;data[num].next=h[x];h[x]=num;data[num].val=val;
        data[++num].to=x;data[num].next=h[y];h[y]=num;data[num].val=val;
    }dfs(1,0);inirmq();dfs1(1,0);rt=0;sumsz=n;dfs2(1,0);fa[rt]=0;getG(rt);
    for(int i=1;i<=n;++i)
        for(int j=i;j;j=fa[j]){
            s1[j].push_back(make_pair(a[i],caldis(i,j)));
            if(fa[j]) s2[j].push_back(make_pair(a[i],caldis(i,fa[j])));
        }
    for(int i=1;i<=n;++i){
        s1[i].push_back(make_pair(-inf,0));s2[i].push_back(make_pair(-inf,0));
        sort(s1[i].begin(),s1[i].end());
        for(int j=1;j<s1[i].size();++j) s1[i][j].second+=s1[i][j-1].second;
        sort(s2[i].begin(),s2[i].end());
        for(int j=1;j<s2[i].size();++j) s2[i][j].second+=s2[i][j-1].second;
    }while(m--){
        int x=read(),l=read(),r=read();
        l=(l+ans)%mod;r=(r+ans)%mod;if(l>r) swap(l,r);
        printf("%lld\n",ans=calc(x,r)-calc(x,l-1));
    }return 0;
}

树链剖分+主席树

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 150010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,m,mod,fa[N],dis[N],dep[N],dfn[N],dfnum=0,tp[N],h[N],num=0,owo=0,rt[N];
int sz[N],son[N],v[N];ll w[N],ans=0,sum[N];
struct edge{
    int to,next,val;
}data[N<<1];
struct mon{
    int id,age;
}a[N];
struct node{
    int lc,rc,cov;;ll sum;//cov-完全覆盖次数,sum-非完全覆盖的和 
}tr[20000010];
inline bool cmp(mon a,mon b){return a.age<b.age;}
inline bool cmp1(int b,mon a){return b<a.age;}
inline void dfs1(int x){
    sz[x]=1;son[x]=0;
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==fa[x]) continue;v[y]=data[i].val;
        fa[y]=x;dep[y]=dep[x]+1;dis[y]=dis[x]+data[i].val;
        dfs1(y);sz[x]+=sz[y];if(sz[y]>sz[son[x]]) son[x]=y;
    }
}
inline void dfs2(int x,int top){
    tp[x]=top;dfn[x]=++dfnum;w[dfnum]=v[x];
    if(son[x]) dfs2(son[x],top);
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==son[x]||y==fa[x]) continue;dfs2(y,y);
    }
}
inline void cover(int &p,int l,int r,int x,int y){
    tr[++owo]=tr[p];p=owo;
    if(x==l&&r==y){tr[p].cov++;return;}
    int mid=l+r>>1;tr[p].sum+=w[y]-w[x-1];
    if(y<=mid) cover(tr[p].lc,l,mid,x,y);
    else if(x>mid) cover(tr[p].rc,mid+1,r,x,y);
    else cover(tr[p].lc,l,mid,x,mid),cover(tr[p].rc,mid+1,r,mid+1,y);
}
inline void docover(int &rt,int x){
    while(tp[x]!=1){
        cover(rt,1,n,dfn[tp[x]],dfn[x]);x=fa[tp[x]];
    }cover(rt,1,n,1,dfn[x]);
}
inline ll qsum(int p,int l,int r,int x,int y){
    if(!p) return 0;
    ll res=(w[y]-w[x-1])*tr[p].cov;
    if(x==l&&r==y) return res+tr[p].sum;
    int mid=l+r>>1;
    if(y<=mid) return res+qsum(tr[p].lc,l,mid,x,y);
    if(x>mid) return res+qsum(tr[p].rc,mid+1,r,x,y);
    return res+qsum(tr[p].lc,l,mid,x,mid)+qsum(tr[p].rc,mid+1,r,mid+1,y);
}
inline ll dosum(int rt,int x){
    ll res=0;
    while(tp[x]!=1){
        res+=qsum(rt,1,n,dfn[tp[x]],dfn[x]);x=fa[tp[x]];
    }return res+qsum(rt,1,n,1,dfn[x]);
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();m=read();mod=read();
    for(int i=1;i<=n;++i) a[i].age=read(),a[i].id=i;
    for(int i=1;i<n;++i){
        int x=read(),y=read(),val=read();
        data[++num].to=y;data[num].next=h[x];h[x]=num;data[num].val=val;
        data[++num].to=x;data[num].next=h[y];h[y]=num;data[num].val=val;
    }dfs1(1);dfs2(1,1);for(int i=1;i<=n;++i) w[i]+=w[i-1];
    sort(a+1,a+n+1,cmp);for(int i=1;i<=n;++i) sum[i]=sum[i-1]+dis[a[i].id];
    for(int i=1;i<=n;++i) rt[i]=rt[i-1],docover(rt[i],a[i].id);
    while(m--){
        int x=read(),l=read(),r=read();
        l=(l+ans)%mod;r=(r+ans)%mod;if(l>r) swap(l,r);
        int t1=upper_bound(a+1,a+n+1,l-1,cmp1)-a-1;
        int t2=upper_bound(a+1,a+n+1,r,cmp1)-a-1;
        ans=(ll)dis[x]*(t2-t1)+sum[t2]-sum[t1];
        printf("%lld\n",ans-=dosum(rt[t2],x)-dosum(rt[t1],x)<<1);
    }return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值