• bzoj3891


    学长的题解:显然,暴力求解的复杂度是无法承受的。
    考虑这样的一种暴力,我们把 z 到根上的点全部打标记,对于 l 到 r 之间的点,向上搜索到第一个有标记的点求出它的深度统计答案。观察到,深度其实就是上面有几个已标记了的点(包括自身)。所以,我们不妨把 z 到根的路径上的点全部 +1,对于 l 到 r 之间的点询问他们到根路径上的点权和。仔细观察上面的暴力不难发现,实际上这个操作具有叠加性,且可逆。也就是说我们可以对于 l 到 r 之间的点 i,将 i 到根的路径上的点全部 +1, 转而询问 z 到根的路径上的点(包括自身)的权值和就是这个询问的答案。把询问差分下,也就是用 [1, r] ? [1, l ? 1] 来计算答案,那么现在我们就有一个明显的解法。从 0 到 n ? 1 依次插入点 i,即将 i 到根的路径上的点全部+1。离线询问答案即可。我们现在需要一个数据结构来维护路径加和路径求和,显然树链剖分或LCT 均可以完成这个任务。树链剖分的复杂度为 O((n + q)· log n · log n),LCT的复杂度为 O((n + q)· log n),均可以完成任务。至此,题目已经被我们完美解决。

    1.树链剖分

    
    
    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #define maxn 50001
    #define mo 201314
    using namespace std;
    int n,m,cnt,head[maxn],to[maxn],nex[maxn];
    int dep[maxn],son[maxn],a[maxn],id[maxn],top[maxn],siz[maxn],fa[maxn];
    struct data{int p,id,z;bool vis;}q[maxn<<1];
    struct result{int ans1,ans2;}ans[maxn];
    struct tree{int l,r,sum,tag;}tr[maxn<<2];
    
    inline bool operator<(const data a,const data b){return a.p<b.p;}
    
    inline void read(int &x){
        char ch=getchar();x=0;
        while(!isdigit(ch))ch=getchar();
        while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    }
    
    void addedge(int u,int v){
        to[++cnt]=v;nex[cnt]=head[u];head[u]=cnt;
    }
    
    void dfs1(int x,int f){
        dep[x]=dep[f]+1;fa[x]=f;siz[x]=1;
        int maxson=-1;
        for(int i=head[x];i;i=nex[i]){
            int y=to[i];
            if(y==f)continue;
            dfs1(y,x);
            siz[x]+=siz[y];
            if(siz[y]>maxson){maxson=siz[y];son[x]=y;}
        }
    }
    
    void dfs2(int x,int topf){
        id[x]=++cnt;
        a[cnt]=x;top[x]=topf;
        if(!son[x])return;
        dfs2(son[x],topf);
        for(int i=head[x];i;i=nex[i]){
            int y=to[i];
            if(y==fa[x]||y==son[x])continue;
            dfs2(y,y);
        }
    }
    
    void buildtr(int now,int l,int r){
        tr[now].l=l;tr[now].r=r;
        if(l==r)return;
        int mid=(l+r)>>1;
        buildtr(now<<1,l,mid);buildtr(now<<1|1,mid+1,r);
    }
    
    void pushdown(int now){
        if(tr[now].l==tr[now].r||!tr[now].tag)return;
        tr[now<<1].sum=(tr[now<<1].sum+(tr[now<<1].r-tr[now<<1].l+1)*tr[now].tag)%mo;tr[now<<1].tag=(tr[now<<1].tag+tr[now].tag)%mo;
        tr[now<<1|1].sum=(tr[now<<1|1].sum+(tr[now<<1|1].r-tr[now<<1|1].l+1)*tr[now].tag)%mo;tr[now<<1|1].tag=(tr[now<<1|1].tag+tr[now].tag)%mo;
        tr[now].tag=0;
    }
    
    void updata(int now,int l,int r){
        pushdown(now);
        if(tr[now].l==l&&tr[now].r==r){
            tr[now].tag++;tr[now].sum+=tr[now].r-tr[now].l+1;
            return;
        }
        int mid=(tr[now].l+tr[now].r)>>1;
        if(mid>=r)updata(now<<1,l,r);else
        if(mid<l)updata(now<<1|1,l,r);else{
            updata(now<<1,l,mid);updata(now<<1|1,mid+1,r);
        }
        tr[now].sum=(tr[now<<1].sum+tr[now<<1|1].sum)%mo;//注意这里(l,r)变成(l,mid)(mid+1,r)因此RE了好几次
    }
    
    void solve_updata(int x,int y){
        while(top[x]!=top[y]){
            updata(1,id[top[x]],id[x]);
            x=fa[top[x]];
        }
        updata(1,id[y],id[x]);
    }
    
    int query(int now,int l,int r){
        pushdown(now);
        if(tr[now].l==l&&tr[now].r==r)return tr[now].sum;
        int mid=(tr[now].l+tr[now].r)>>1;
        if(mid>=r)return query(now<<1,l,r);
        if(mid<l)return query(now<<1|1,l,r);
        return query(now<<1,l,mid)+query(now<<1|1,mid+1,r);//注意这里(l,r)变成(l,mid+1)和(mid+1,r)因此RE了好几次
    }
    
    int solve_query(int x,int y){
        int res=0;
        while(top[x]!=top[y]){
            res=(res+query(1,id[top[x]],id[x]))%mo;
            x=fa[top[x]];
        }
        res=(res+query(1,id[y],id[x]))%mo;
        return res;
    }
    
    int main(){
        read(n);read(m);
        for(int i=1;i<n;i++){
            int x;read(x);addedge(x,i);
        }
        cnt=0;
        dfs1(0,0);dfs2(0,0);
        cnt=0;
        for(int i=1;i<=m;i++){
            int l,r,z;read(l);read(r);read(z);
            q[++cnt].p=l-1;q[cnt].id=i;q[cnt].z=z;q[++cnt].p=r;q[cnt].id=i;q[cnt].z=z;q[cnt].vis=1;
        }
        sort(q+1,q+cnt+1);
        buildtr(1,1,n);
        int now=-1;
        for(int i=1;i<=cnt;i++){
            while(now<q[i].p)solve_updata(++now,0);
            int id=q[i].id;
            if(!q[i].vis)ans[id].ans1=solve_query(q[i].z,0);else ans[id].ans2=solve_query(q[i].z,0);
        }
        for(int i=1;i<=m;i++)printf("%d\n",(ans[i].ans2-ans[i].ans1+mo)%mo);
    }
    
    
    
    
    
  • 相关阅读:
    【转】彻底解决matplotlib中文乱码问题
    angularjs依赖注入,setInterval()功能
    【转】图解SQL的各种连接join
    关于c#调用matlab时,deploytool选项没有.NET Assembly的问题的解决
    SQL从其他服务器数据库导入数据到本地数据库中
    【转】Asp.net下载文件、文件流输出 直接输出文件
    【转】正则应用实例,如将多个空格改为1个空格
    【转】线程间操作无效:从不是创建控件 的线程访问它,解决办法
    js获取鼠标坐标,设置div的高度、位置、内容等,及注意要点
    Java线程阻塞的方法
  • 原文地址:https://www.cnblogs.com/MikuKnight/p/9010922.html
Copyright © 2020-2023  润新知