• 树链剖分


    搞了好几天终于把树剖模板过了
    感觉最难理解的是对两点之间路径上的修改,我就是在这里被卡了好久
    还是看注释吧
    原题链接:https://www.luogu.org/problemnew/show/P3384

    #include<bits/stdc++.h>
    #define mid ((l+r)>>1)
    #define sw(x,y) jzm=x,x=y,y=jzm;
    #define For(i,j,k) for(int i=j;i<=k;++i)
    using namespace std;
    int read(){
        int x=0,l=1; char ch=getchar();
        while(!isdigit(ch)) {if (ch=='-') l=-1; ch=getchar();}
        while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
        return x*l;
    }
    typedef long long ll;
    const int N=100005;
    struct node{
        ll w,en;
    }a[N*3];
    int jzm,e,b[N],head[N],to[N<<1],nex[N<<1],mp[N],mp2[N];
    void ad(int x,int y){
        to[++e]=y; nex[e]=head[x]; head[x]=e;
    }
    void bt(int x,int l,int r){
        if (l==r) {a[x].w=b[mp2[l]]; return;}
        bt(x<<1,l,mid); bt(x<<1|1,mid+1,r);
        a[x].w=a[x<<1].w+a[x<<1|1].w;
    }
    int xx,yy,n,m,r,p; ll k;
    void pd(int fl,int l,int r){
        a[fl<<1].w+=a[fl].en*(mid-l+1);
        a[fl<<1|1].w+=a[fl].en*(r-mid);
        a[fl<<1].en+=a[fl].en;
        a[fl<<1|1].en+=a[fl].en;
        a[fl].en=0;
    }
    void add(int fl,int l,int r){
        if (l>=xx&&r<=yy){
            a[fl].w+=k*(r-l+1); a[fl].en+=k; return;
        }
        if (a[fl].en) pd(fl,l,r);
        if (mid>=xx) add(fl<<1,l,mid);
        if (mid<yy) add(fl<<1|1,mid+1,r);
        a[fl].w=a[fl<<1].w+a[fl<<1|1].w;
    }
    ll qu(int fl,int l,int r){
        if (l>=xx&&r<=yy) return a[fl].w;
        ll an=0;
        if (a[fl].en) pd(fl,l,r);
        if (mid>=xx) an+=qu(fl<<1,l,mid);
        if (mid<yy) an+=qu(fl<<1|1,mid+1,r);
        return an;
    }
    int cnt,siz[N],son[N],top[N],dep[N],fa[N];
    void dfs1(int x,int de,int f){
        siz[x]=1; dep[x]=de++; fa[x]=f; int ma=0;
        for(int i=head[x];i;i=nex[i])
            if (to[i]!=f){
                dfs1(to[i],de,x);
                if (ma<siz[to[i]]) ma=siz[to[i]],son[x]=to[i];
                siz[x]+=siz[to[i]];
            }
    }
    void dfs2(int x,int tp){
        mp[x]=++cnt; top[x]=tp;
        if (son[x]){
            dfs2(son[x],tp);
            for(int i=head[x];i;i=nex[i])
                if (to[i]!=fa[x]&&to[i]!=son[x]) dfs2(to[i],to[i]);
        }
    }
    int main(){
        int x,y,fl,hk,wal,lca,de,f; ll ans; n=read(),m=read(),r=read(),p=read();
        For(i,1,n) b[i]=read();
        For(i,1,n-1){
            x=read(),y=read();
            ad(x,y); ad(y,x);
        }
        dfs1(r,1,0); dfs2(r,r);
        For(i,1,n) mp2[mp[i]]=i; bt(1,1,n);
        For(i,1,m){
            fl=read(),x=read();
            if (fl==3) xx=mp[x],yy=xx+siz[x]-1,k=read(),add(1,1,n);
            else if (fl==4) xx=mp[x],yy=xx+siz[x]-1,printf("%lld
    ",qu(1,1,n)%p);
            else if (fl==2){
                y=read(); ans=0;
                while(top[x]!=top[y]){//当两个点不在同一条链上 
                    if(dep[top[x]]<dep[top[y]]) sw(x,y);//把x点改为所在链顶端的深度更深的那个点
                    xx=mp[top[x]]; yy=mp[x]; ans+=qu(1,1,n);//ans加上x点到x所在链顶端 这一段区间的点权和
                    x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
                }//直到两个点处于一条链上
                if(dep[x]>dep[y]) sw(x,y);//把x点变为深度更浅的那个点
                xx=mp[x]; yy=mp[y]; ans+=qu(1,1,n);//这时再加上此时两个点的区间和即可
                printf("%lld
    ",ans%p);
            }
            //接下来同上
            else{
                y=read(); k=read();
                while(top[x]!=top[y]){
                    if(dep[top[x]]<dep[top[y]]) sw(x,y);
                    xx=mp[top[x]]; yy=mp[x]; add(1,1,n);
                    x=fa[top[x]];
                }
                if(dep[x]>dep[y]) sw(x,y);
                xx=mp[x]; yy=mp[y]; add(1,1,n);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    java基础(一)
    html脚本总结
    python编码规范以及推导式的编写
    性能测试
    IOS 单例分析
    IOS 从一个应用跳转另一个应用
    ios开发 如何在应用内获取当前周围wifi列表和强度 并实现在应用内控制wifi开关
    iOS 获取手机的型号,系统版本,软件名称,软件版本
    ios下最简单的正则,RegexKitLite
    网络编程总结(解析数据,下载文件,确认网络环境)
  • 原文地址:https://www.cnblogs.com/wjy666/p/7989279.html
Copyright © 2020-2023  润新知