LOJ#6046. 「雅礼集训 2017 Day8」爷

Description

给你一个$n$个点的有根树,$1$为根,带边权,有$m$次操作。

求$x$的子树中第$k$小的深度的值,如果子树中没有$k$个点则输出$-1$;
将$x$与$x$父亲的边权加上$k$。
保证每次操作 2 的$k$以及原树的边权小于等于一个数$len$。

如果操作 2 中$x$为$1$,那么视为将$x$的基础深度加上了$k$。

Solution

先用dfs序将其转化为区间上的问题

如果直接分块并在块内排序,每次查找时二分答案再在每个块内二分,复杂度是$O(n \sqrt{n} \log^2 n)$的

题中说边权以及每次加的值不是很大

可以控制每个块内极差在一定范围内,维护块内前缀和,就可以做到查询时复杂度$O(n \sqrt{n} \log n)$

需要定期地对整个序列重建分块

#include<iostream>
#include<utility>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,K,head[100005],tot,ll[100005],rr[100005],dfn,a[100005],cnt,plz[100005],tik,B,bot[100005],top[100005],sum[1005][16005],bel[100005],bg[100005],ed[100005],sta1[100005],sta2[100005],top1,top2,lim;
const int inf=0x7f7f7f7f;
struct Edge{
    int to,nxt,w;
}edge[200005];
inline int read(){
    int f=1,w=0;
    char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
    return f*w;
}
void solve(int id){
    top[id]=-inf,bot[id]=inf;
    for(int j=bg[id];j<=ed[id];j++)top[id]=max(top[id],a[j]),bot[id]=min(bot[id],a[j]);
    memset(sum[id],0,(top[id]-bot[id]+1)<<2);
    for(int j=bg[id];j<=ed[id];j++)++sum[id][a[j]-bot[id]];
    for(int j=1;j<=top[id]-bot[id];j++)sum[id][j]+=sum[id][j-1];
}
void rebuild(){
    for(int i=1;i<=n;i++)a[i]+=plz[bel[i]];
    memset(plz,0,(cnt+1)<<2),memset(bg,0,(cnt+1)<<2),cnt=tik=0,cnt=1;
    int maxx=a[1],minn=a[1],len=0;
    for(int i=1;i<=n;i++){
        ++len,bel[i]=cnt,ed[cnt]=i,maxx=max(maxx,a[i+1]),minn=min(minn,a[i+1]);
        if(!bg[cnt])bg[cnt]=i;
        if(maxx-minn+1>=B*K||len>=B||i==n)solve(cnt),maxx=minn=a[i+1],len=0,++cnt;
    }
    --cnt;
}
void dfs(int k,int dep){
    ll[k]=++dfn,a[dfn]=dep,lim=max(lim,dep);
    for(int i=head[k];i;i=edge[i].nxt)dfs(edge[i].to,dep+edge[i].w);
    rr[k]=dfn;
}
int calc(int x){
    int ret=0;
    for(int i=1;i<=top1;i++){
        if(x<plz[sta1[i]]+bot[sta1[i]])continue;
        if(x>=plz[sta1[i]]+top[sta1[i]])ret+=ed[sta1[i]]-bg[sta1[i]]+1;
        else ret+=sum[sta1[i]][x-plz[sta1[i]]-bot[sta1[i]]];
    }
    for(int i=1;i<=top2;i++)ret+=(sta2[i]<=x);
    return ret;
}
int query(int l,int r,int k){
    top1=top2=0;
    for(int i=l;i<=r;i=ed[bel[i]]+1)
        if(i==bg[bel[i]]&&ed[bel[i]]<=r)sta1[++top1]=bel[i];
        else for(int j=i;j<=min(r,ed[bel[i]]);j++)sta2[++top2]=a[j]+plz[bel[i]];
    int L=0,R=lim,ret=0;
    while(L<=R){
        int mid=L+R>>1;
        if(calc(mid)>=k)ret=mid,R=mid-1;
        else L=mid+1;
    }
    return ret;
}
void update(int l,int r,int k){
    lim+=k;
    for(int i=l;i<=r;i=ed[bel[i]]+1)
        if(i==bg[bel[i]]&&ed[bel[i]]<=r)plz[bel[i]]+=k;
        else{
            for(int j=i;j<=min(r,ed[bel[i]]);j++)a[j]+=k;
            for(int j=bg[bel[i]];j<=ed[bel[i]];j++)a[j]+=plz[bel[i]];
            plz[bel[i]]=0,solve(bel[i]);
        }
}
int main(){
    n=read(),m=read(),K=read(),B=sqrt(n);
    for(int i=2;i<=n;i++){
        int x=read(),w=read();
        edge[++tot]=(Edge){i,head[x],w},head[x]=tot;
    }
    dfs(1,0),rebuild();
    for(;m;m--){
        int opt=read(),x=read(),k=read();
        if(opt==1)printf("%d\n",rr[x]-ll[x]+1>=k?query(ll[x],rr[x],k):-1);
        else{
            update(ll[x],rr[x],k),++tik;
            if(tik>=1000)rebuild();
        }
    }
    return 0;
}
「雅礼集训 2017 Day8」爷

 

posted @ 2021-03-14 10:59  QDK_Storm  阅读(171)  评论(0编辑  收藏  举报