LNOI2014 LCA

LNOI2016 LCA

好题啊qaq

就是主席树的区间修改啊

蛤?不会主席树区间修改?我也不会qaq

好像不太容易进行pushdown操作

但是有一个好东西叫做标记永久化啊!

于是学习一波姿势  :

https://www.cnblogs.com/Hallmeow/p/8004676.html

https://www.cnblogs.com/wozaixuexi/p/9462461.html

假设每个节点维护一个sum和add

核心思想就是修改的时候沿途修改经过的节点的sum

当修改区间与当前区间完全重合时更新add标记

查询的时候沿途累加add标记,返回sum+add*len的和

求(u,v)的 LCA 的一个技巧是将u到根节点的路径打上标记,查询v到根节点的和,显然树剖+线段树嘛(当然更可以LCT啦)

这题其实可以询问离线,把(l,r) 的询问拆成 (1,l-1) 和 (1,r) ,这样可以按时间顺序向线段树中插入节点

但这题还有一个加强版:HNOI2015开店  强制在线

那怎么办啊qaq

主席树嘛,区间修改的主席树嘛,于是这道题我也是这么写的

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;

const int maxn = 100100;
const int mod = 201314;

int n,q;

int h[maxn],size;
struct E{
    int to,next;
}e[maxn];
void add_edge(int u,int v){
    e[++size].to=v;
    e[size].next=h[u];
    h[u]=size;
}

int rt[maxn],lc[maxn*150],rc[maxn*150],sum[maxn*150],add[maxn*150],tot;

int fa[maxn],dep[maxn],sz[maxn],son[maxn];
int dfn[maxn],top[maxn],id[maxn],tim;

void dfs1(int u,int par){
    fa[u]=par; dep[u]=dep[par]+1; sz[u]=1;
    int maxs=-1;
    for(int i=h[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(v==par) continue;
        dfs1(v,u);
        sz[u]+=sz[v];
        if(sz[v]>maxs){
            maxs=sz[v];
            son[u]=v;
        }
    }
}

void dfs2(int u,int par){
    top[u]=par; dfn[u]=++tim; id[tim]=u;
    if(son[u]) dfs2(son[u],par);
    for(int i=h[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

void build(int &i,int l,int r){
    i=++tot;
    if(l==r) return;
    int mid=(l+r)/2;
    build(lc[i],l,mid);
    build(rc[i],mid+1,r);
}

void mdf(int &i,int l,int r,int x,int y){
    sum[++tot]=sum[i],lc[tot]=lc[i],rc[tot]=rc[i],add[tot]=add[i];
    i=tot;
    sum[i]+=(y-x+1); sum[i]%=mod;
    if(x==l&&r==y){ ++add[i]; return; }
    int mid=(l+r)/2;
    if(x>mid) mdf(rc[i],mid+1,r,x,y);
    else if(y<=mid) mdf(lc[i],l,mid,x,y);
    else{
        mdf(lc[i],l,mid,x,mid);
        mdf(rc[i],mid+1,r,mid+1,y);
    }
}

int qry(int i,int ad,int l,int r,int x,int y){
    if(l==x&&r==y){     return (sum[i]+1ll*ad*(r-l+1)%mod)%mod; }
    int mid=(l+r)/2;
    if(x>mid) return qry(rc[i],(ad+add[i])%mod,mid+1,r,x,y);
    else if(y<=mid) return qry(lc[i],(ad+add[i])%mod,l,mid,x,y);
    else{ return qry(lc[i],(ad+add[i])%mod,l,mid,x,mid)+qry(rc[i],ad+add[i],mid+1,r,mid+1,y); }
}

void upd(int i,int u,int v){
    while(top[u]^top[v]){
        if(dep[u]<dep[v]) swap(u,v);
        mdf(rt[i],1,n,dfn[top[u]],dfn[u]);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    mdf(rt[i],1,n,dfn[u],dfn[v]);
}

int query(int i,int u,int v){
    int res=0;
    while(top[u]^top[v]){
        if(dep[u]<dep[v]) swap(u,v);
        res+=qry(rt[i],0,1,n,dfn[top[u]],dfn[u]); res%=mod;
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    res+=qry(rt[i],0,1,n,dfn[u],dfn[v]); res%=mod;
    return res;
}

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }

int main(){
    memset(h,-1,sizeof(h));
    n=read(),q=read();
    int u,v,z;
    for(int i=2;i<=n;i++){
        u=read(); ++u;
        add_edge(i,u),add_edge(u,i);
    }
    
    dfs1(1,0); dfs2(1,1);
    build(rt[0],1,n);
    for(int i=1;i<=n;i++){ rt[i]=rt[i-1]; upd(i,1,i); }
    
    for(int i=1;i<=q;i++){
        u=read()+1,v=read()+1,z=read()+1;
        printf("%d\n",(query(v,1,z)-query(u-1,1,z)+mod)%mod);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/10545077.html