树链剖分

练习一下无脑的水分方法。
树链剖分。
平时用来求LCA还是好用的。

T1 RG

分析

求LCA,然后下标为颜色建树,线段树合并
树链剖分只是拿来求LCA的
结果内存不够,
这个时候应该用内存池来存那些无用节点然后再拿出来用。
所以就可以写了。
高端卡内存技巧
复杂度为 O((n+m)logn)

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

#define M 100004
#define S 40
#define pb push_back
#define SZ(a) ((int)(a).size())

int n,m,q;
vector<int>P[M];
int Lp[M*S],Rp[M*S],Mx[M*S],Cnt[M*S],trp;
int Stk[M*S],tp;//内存池

#define lson l,mid,Lp[p]
#define rson mid+1,r,Rp[p]

void Up(int p){
    if(Cnt[Rp[p]]>Cnt[Lp[p]])Cnt[p]=Cnt[Rp[p]],Mx[p]=Mx[Rp[p]];
    else Cnt[p]=Cnt[Lp[p]],Mx[p]=Mx[Lp[p]];
}
void Insert(int l,int r,int &p,int old,int x){
    if(tp) p=Stk[tp--];//使用旧节点
    else p=++trp;
    if(l==r){
        Mx[p]=l;
        Cnt[p]=Cnt[old]+(x<0?-1:1);
        return;
    }
    int mid=l+r>>1;
    if(abs(x)<=mid)Rp[p]=Rp[old],Insert(lson,Lp[old],x);
    else Lp[p]=Lp[old],Insert(rson,Rp[old],x);
    Up(p);
    if(old)Stk[++tp]=old;//回收旧节点
}
int Merge(int l,int r,int p,int q){
    if(!p || !q)return p|q;
    Stk[++tp]=q;//回收旧节点
    if(l==r){
        Cnt[p]+=Cnt[q];
        return p;
    }
    int mid=l+r>>1;
    Lp[p]=Merge(lson,Lp[q]);
    Rp[p]=Merge(rson,Rp[q]);
    Up(p);
    return p;
}

int Next[M<<1],V[M<<1],Head[M],tot;
void Add_Edge(int u,int v){
    Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
int Sz[M],Son[M],Top[M],Fa[M],Dep[M];

void DFS(int A,int f){
    Sz[A]=1;
    Dep[A]=Dep[f]+1;
    Fa[A]=f;
    Son[A]=0;
    int B;
    LREP(i,A)if((B=V[i])!=f){
        DFS(B,A);
        Sz[A]+=Sz[B];
        if(Sz[B]>Sz[Son[A]])Son[A]=B;
    }
}
void FindTop(int A,int tp){
    Top[A]=tp;
    if(Son[A])FindTop(Son[A],tp);
    int B;
    LREP(i,A)if((B=V[i])!=Fa[A] && B!=Son[A])
        FindTop(B,B);
}
int LCA(int A,int B){
    while(Top[A]!=Top[B])
        if(Dep[Top[A]]>Dep[Top[B]])A=Fa[Top[A]];
        else B=Fa[Top[B]];
    return Dep[A]<Dep[B]?A:B;
}

int Ans[M],Rt[M];
void Answer(int A){
    int B;
    if(tp)Rt[A]=Stk[tp--];
    else Rt[A]=++trp;
    Mx[Rt[A]]=Cnt[Rt[A]]=Lp[Rt[A]]=Rp[Rt[A]]=0;

    LREP(i,A)if((B=V[i])!=Fa[A])
        Answer(B),Rt[A]=Merge(1,m,Rt[A],Rt[B]);

    REP(i,0,SZ(P[A]))
        Insert(1,m,Rt[A],Rt[A],P[A][i]);

    Ans[A]=Mx[Rt[A]];
}
int main(){
    while(scanf("%d%d",&n,&q)!=EOF){
        if(!n)break;
        memset(Head,tot=tp=trp=0,sizeof(Head));
        REP(i,0,n+1)vector<int>().swap(P[i]);

        REP(i,1,n){
            int u,v;
            Rd(u),Rd(v);
            Add_Edge(u,v);
            Add_Edge(v,u);
        }

        DFS(1,0);
        FindTop(1,1);

        m=0;
        while(q--){
            int u,v,z,c;
            Rd(u),Rd(v),Rd(z);
            c=LCA(u,v);
            P[u].pb(z);
            P[v].pb(z);
            P[c].pb(-z);
            P[Fa[c]].pb(-z);
            chkmax(m,z);
        }

        Answer(1);
        REP(i,1,n+1)printf("%d\n",Ans[i]);
    }
    return 0;
}

T2 TWNMZ

分析

十分玄学的一道题。
正常写法是树链剖分然后分块维护。
不过玄学写法更快(?)
这份代码采用的是单调栈暴力加直接暴力。
用了vector跑了4s。

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

#define M 100004
#define MS 20

int n,m,q,T,Val[M];

int Next[M<<1],V[M<<1],Head[M],tot;
void Add_Edge(int u,int v){
    Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
int Sz[M],Son[M],Top[M],Fa[M],Dep[M];

void DFS(int A,int f){
    Sz[A]=1;
    Dep[A]=Dep[f]+1;
    Fa[A]=f;
    Son[A]=0;
    int B;
    LREP(i,A)if((B=V[i])!=f){
        DFS(B,A);
        Sz[A]+=Sz[B];
        if(Sz[B]>Sz[Son[A]])Son[A]=B;
    }
}
void FindTop(int A,int tp){
    Top[A]=tp;
    if(Son[A])FindTop(Son[A],tp);
    int B;
    LREP(i,A)if((B=V[i])!=Fa[A] && B!=Son[A])
        FindTop(B,B);
}
int LCA(int A,int B){
    while(Top[A]!=Top[B])
        if(Dep[Top[A]]>Dep[Top[B]])A=Fa[Top[A]];
        else B=Fa[Top[B]];
    return Dep[A]<Dep[B]?A:B;
}

struct Question{
    int u,v,k,id;
    bool operator <(const Question &_)const{
        return k<_.k;
    }
}Qes[M];
#define pb push_back
#define SZ(a) ((int)(a).size())
struct Node{int Lim,elen,k,id;};
vector<Node>Q[M];
bool Cmp(int a,int b){return Val[a]>Val[b];}
bool Cmp2(int a,int b){return Dep[a]<Dep[b];}
int Ans[M],Nowk;
int Stk[MS][M],Tp[MS];
int Use[M],tp;

void Answer(int A){
    Use[++tp]=A;
    int p=Dep[A]%Nowk,w=lower_bound(Stk[p],Stk[p]+Tp[p],A,Cmp)-Stk[p];
    int Tmp=Stk[p][w],Tmpl=Tp[p],B;
    Stk[p][w]=A,Tp[p]=w+1;

    LREP(i,A)if((B=V[i])!=Fa[A])
        Answer(B);

    REP(i,0,SZ(Q[A])){
        Node TQ=Q[A][i];
        if(!TQ.elen){
            int wt=lower_bound(Stk[p],Stk[p]+Tp[p],TQ.Lim,Cmp2)-Stk[p];
            chkmax(Ans[TQ.id],Val[Stk[p][wt]]);
        }
        else if(Dep[A]-TQ.elen>=Dep[TQ.Lim]){
            int re=tp-TQ.elen;TQ.elen=0;
            Q[Use[re]].pb(TQ);
        }
    }
    vector<Node>().swap(Q[A]);

    Stk[p][w]=Tmp,Tp[p]=Tmpl;
    tp--;
}
void Find(int A){
    int B;
    Use[++tp]=A;

    LREP(i,A)if((B=V[i])!=Fa[A])
        Find(B);

    REP(i,0,SZ(Q[A])){
        Node TQ=Q[A][i];
        if(!TQ.elen){
            chkmax(Ans[TQ.id],Val[A]);
            if(Dep[A]-TQ.k>=Dep[TQ.Lim])Q[Use[tp-TQ.k]].pb(TQ);
        }
        else if(Dep[A]-TQ.elen>=Dep[TQ.Lim]){
            int re=tp-TQ.elen;TQ.elen=0;
            Q[Use[re]].pb(TQ);
        }
    }
    vector<Node>().swap(Q[A]);
    tp--;
}
int main(){
    Rd(T);
    REP(Case,1,T+1){
        memset(Head,tot=0,sizeof(Head));

        Rd(n),Rd(q);
        REP(i,1,n+1)Rd(Val[i]);

        REP(i,1,n){
            int u,v;
            Rd(u),Rd(v);
            Add_Edge(u,v);
            Add_Edge(v,u);
        }

        DFS(1,0);
        FindTop(1,1);

        REP(i,0,q)Rd(Qes[i].u),Rd(Qes[i].v),Rd(Qes[i].k),Qes[i].id=i;

        memset(Ans,0,sizeof(Ans));
        sort(Qes,Qes+q);

        int i=0;
        while(i<q){
            if(Qes[i].k>=MS)break;
            Nowk=Qes[i].k;
            while(i<q && Qes[i].k==Nowk){
                int u=Qes[i].u,v=Qes[i].v,c=LCA(u,v),d=(Dep[u]+Dep[v]-(Dep[c]<<1)+1)%Qes[i].k;
                Q[u].pb((Node){c,Nowk-1,Qes[i].k,Qes[i].id});
                Q[v].pb((Node){c,d,Qes[i].k,Qes[i].id});
                i++;
            }
            Answer(1);
        }
        if(i!=q){
            while(i<q){
                int u=Qes[i].u,v=Qes[i].v,c=LCA(u,v),d=(Dep[u]+Dep[v]-(Dep[c]<<1)+1)%Qes[i].k;
                Q[u].pb((Node){c,Qes[i].k-1,Qes[i].k,Qes[i].id});
                Q[v].pb((Node){c,d,Qes[i].k,Qes[i].id});
                i++;
            }
            Find(1);
        }

        printf("Case #%d:\n",Case);
        REP(j,0,q)printf("%d\n",Ans[j]);
    }
    return 0;
}

T3 CSG

这个显然是主席树裸题
树链剖分的写法:
重排序权值一维作为时间维,然后直接树链剖分求和。

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

#define M 100004
#define S 20

int n,m,q,Val[M],P[M];
int Lp[M*S],Rp[M*S],Rt[M],trp;
LL Sum[M*S];

int Next[M<<1],V[M<<1],Head[M],tot;
void Add_Edge(int u,int v){
    Next[++tot]=Head[u],V[Head[u]=tot]=v;
}
#define LREP(i,A) for(int i=Head[A];i;i=Next[i])
int Sz[M],Son[M],Top[M],Fa[M],Dep[M];

#define lson l,mid,Lp[p]
#define rson mid+1,r,Rp[p]
void Insert(int l,int r,int &p,int old,int t){
    p=++trp;
    if(l==r){
        Sum[p]=Sum[old]+P[l];
        return;
    }
    int mid=l+r>>1;
    if(t<=mid)Rp[p]=Rp[old],Insert(lson,Lp[old],t);
    else Lp[p]=Lp[old],Insert(rson,Rp[old],t);
    Sum[p]=Sum[Lp[p]]+Sum[Rp[p]];
}
LL Query(int l,int r,int p,int a,int b){
    if(!p || l>b || r<a)return 0;
    if(a<=l && r<=b)return Sum[p];
    int mid=l+r>>1;
    return Query(lson,a,b)+Query(rson,a,b);
}

void DFS(int A,int f){
    Insert(1,m,Rt[A],Rt[f],Val[A]);
    Sz[A]=1;
    Dep[A]=Dep[f]+1;
    Fa[A]=f;
    Son[A]=0;
    int B;
    LREP(i,A)if((B=V[i])!=f){
        DFS(B,A);
        Sz[A]+=Sz[B];
        if(Sz[B]>Sz[Son[A]])Son[A]=B;
    }
}
void FindTop(int A,int tp){
    Top[A]=tp;
    if(Son[A])FindTop(Son[A],tp);
    int B;
    LREP(i,A)if((B=V[i])!=Fa[A] && B!=Son[A])
        FindTop(B,B);
}
int LCA(int A,int B){
    while(Top[A]!=Top[B])
        if(Dep[Top[A]]>Dep[Top[B]])A=Fa[Top[A]];
        else B=Fa[Top[B]];
    return Dep[A]<Dep[B]?A:B;
}

int main(){
    while(scanf("%d",&n)!=EOF){

        memset(Head,tot=trp=0,sizeof(Head));
        Rd(q);
        REP(i,1,n+1)Rd(Val[i]),P[i]=Val[i];
        sort(P+1,P+n+1);
        m=unique(P+1,P+n+1)-P-1;
        REP(i,1,n+1)Val[i]=lower_bound(P+1,P+m+1,Val[i])-P;

        REP(i,1,n){
            int u,v;
            Rd(u),Rd(v);
            Add_Edge(u,v);
            Add_Edge(v,u);
        }

        DFS(1,0);
        FindTop(1,1);

        while(q--){
            int x,y,c,a,b;
            Rd(x),Rd(y),Rd(a),Rd(b);
            a=lower_bound(P+1,P+m+1,a)-P,b=upper_bound(P+1,P+m+1,b)-P-1;
            c=LCA(x,y);
            printf("%lld",Query(1,m,Rt[x],a,b)+Query(1,m,Rt[y],a,b)-Query(1,m,Rt[c],a,b)-Query(1,m,Rt[Fa[c]],a,b));
            if(q)putchar(' ');
        }
        puts("");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值