树链剖分总结

树链剖分总结


树链剖分:

已知一棵包含N个结点的树,每个节点具有点权:

操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z

操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和

操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z

操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和

(操作3,4可以用“dfs序 + 线段树”。)

即涉及两点间路径,子树内的点权的题。

与lca相比,多了修改操作。

复杂度:单次修改和查询均为O(logn*logn)

序列:query(x,y)

子树:tquery(dfn[x],dfn[x] + sz[x] - 1)

const ll maxn = 1e5 + 10;

ll n,m,r,p,k,idx;
ll a[maxn],head[maxn],dfn[maxn],top[maxn],d[maxn],f[maxn],sz[maxn],son[maxn],pos[maxn];

struct edge{
	ll v,nxt;
}e[maxn << 1];
void adde(ll u,ll v){
	e[k].v = v;
	e[k].nxt = head[u];
	head[u] = k++;
}

struct segment_tree{//based on dfn
	ll sum,lzy;
}t[maxn << 2];

void pushup(ll u){
	t[u].sum = t[u << 1].sum + t[u << 1 | 1].sum;
}

void pushdown(ll u,ll m){
	if(t[u].lzy){
		t[u << 1].lzy += t[u].lzy;
		t[u << 1 | 1].lzy += t[u].lzy;
		t[u << 1].sum += t[u].lzy * (m - (m >> 1));
		t[u << 1 | 1].sum += t[u].lzy * (m >> 1);
		t[u].lzy = 0;
	}
}

void dfs1(ll u,ll fa){
	d[u] = d[fa] + 1;f[u] = fa;
	sz[u] = 1;//!!
	for(ll i = head[u]; i != -1; i = e[i].nxt){
		ll v = e[i].v;
		if(v != fa){
			dfs1(v,u);
			sz[u] += sz[v];
			if(sz[son[u]] < sz[v]) son[u] = v;
		}
	}
}

void dfs2(ll u,ll tp){
	dfn[u] = ++idx;pos[idx] = u;top[u] = tp;
	if(son[u])	dfs2(son[u],tp);
	for(ll i = head[u]; i != -1; i = e[i].nxt){
		ll v = e[i].v;
		if(v != f[u] && v != son[u]){
			dfs2(v,v);
		}
	}
}

void tbuild(ll u,ll l,ll r){
	t[u].lzy = 0;
	if(l == r) {
		t[u].sum = a[pos[l]];//!!
		return;
	}
	ll mid = (l + r) >> 1;
	tbuild(lson);tbuild(rson);
	pushup(u);
}

void tupdate(ll u,ll l,ll r,ll a,ll b,ll c){
	if(a <= l && r <= b){
		t[u].sum += (r - l + 1) * c;
		t[u].lzy += c;
		return;
	}
	ll mid = (l + r) >> 1;
	pushdown(u,r - l + 1);
	if(a <= mid) tupdate(lson,a,b,c);
	if(mid < b) tupdate(rson,a,b,c);
	pushup(u);
}

ll tquery(ll u,ll l,ll r,ll a,ll b){
	if(a <= l && r <= b)	return t[u].sum;
	ll mid = (l + r) >> 1,ret = 0;
	pushdown(u,r - l + 1);
	if(a <= mid) ret += tquery(lson,a,b);
	if(mid < b) ret += tquery(rson,a,b);
	return ret;
}

void update(ll x,ll y,ll w){
	while(top[x] != top[y]){
		if(d[top[x]] < d[top[y]]) swap(x,y);
		tupdate(1,1,n,dfn[top[x]],dfn[x],w);
		x = f[top[x]];
	}
	if(d[x] < d[y]) swap(x,y);
	tupdate(1,1,n,dfn[y],dfn[x],w);
}

ll query(ll x,ll y){
	ll ret = 0;
	while(top[x] != top[y]){
		if(d[top[x]] < d[top[y]]) swap(x,y);
		ret += tquery(1,1,n,dfn[top[x]],dfn[x]);
		x = f[top[x]];
	}
	if(d[x] < d[y]) swap(x,y);
	ret += tquery(1,1,n,dfn[y],dfn[x]);
	return ret;	
}

ll x,y,z,op;

int main(){
//	freopen("a.txt","r",stdin);
	memset(head,-1,sizeof(head));
	n = read();m = read();r = read();p = read();
	for(ll i = 1; i <= n; ++i) a[i] = read();
	for(ll i = 1; i < n; ++i){
		x = read();y = read();
		adde(x,y);adde(y,x);
	}
	dfs1(r,0);dfs2(r,r);tbuild(1,1,n);
	while(m--){
		op = read();
		if(op == 1){
			x = read();y = read();z = read();
			update(x,y,z);
		}
		else if(op == 2){
			x = read();y = read();
			printf("%lld\n",query(x,y));
		}
		else if(op == 3){
			x = read();z = read();
			tupdate(1,1,n,dfn[x],dfn[x] + sz[x] - 1,z);
		}
		else {
			x = read();
			printf("%lld\n",tquery(1,1,n,dfn[x],dfn[x] + sz[x] - 1));
		}
	}
	return 0;
}

HYSBZ 3626 LCA
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求 ∑ i l &lt; = i &lt; = r d e p [ L C A ( i , z ) ] \sum_i^{l&lt;=i&lt;=r}dep[LCA(i,z)] il<=i<=rdep[LCA(i,z)]
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)

lca(x,y)的深度=x到根的每条边由0变成1后,y到根的边权和。差分一下,树剖即可。

注意:此题是0到n - 1

const ll maxn = 1e5 + 10;
const ll p = 201314;

ll n,m,k,idx;
ll head[maxn],dfn[maxn],top[maxn],d[maxn],f[maxn],sz[maxn],son[maxn],pos[maxn];

struct edge{
	ll v,nxt;
}e[maxn << 1];
void adde(ll u,ll v){
	e[k].v = v;
	e[k].nxt = head[u];
	head[u] = k++;
}
struct segment_tree{//based on dfn
    ll sum,lzy;
}t[maxn << 2];

void pushup(ll u){
    t[u].sum = t[u << 1].sum + t[u << 1 | 1].sum;
    t[u].sum %= p;
}

void pushdown(ll u,ll m){
    if(t[u].lzy){
        t[u << 1].lzy += t[u].lzy;
        t[u << 1 | 1].lzy += t[u].lzy;
        t[u << 1].sum += t[u].lzy * (m - (m >> 1));
        t[u << 1].sum %= p;
        t[u << 1 | 1].sum += t[u].lzy * (m >> 1);
        t[u << 1 | 1].sum %= p;
        t[u].lzy = 0;
    }
}

void dfs1(ll u,ll fa){
    d[u] = d[fa] + 1;f[u] = fa;sz[u] = 1;//!!
    for(ll i = head[u]; i != -1; i = e[i].nxt){
        ll v = e[i].v;
        if(v != fa){
            dfs1(v,u);
            sz[u] += sz[v];
            if(sz[son[u]] < sz[v]) son[u] = v;
        }
    }
}

void dfs2(ll u,ll tp){
    dfn[u] = ++idx;pos[idx] = u;top[u] = tp;
    if(son[u])	dfs2(son[u],tp);
    for(ll i = head[u]; i != -1; i = e[i].nxt){
        ll v = e[i].v;
        if(v != f[u] && v != son[u]){
            dfs2(v,v);
        }
    }
}

void tbuild(ll u,ll l,ll r){
    t[u].lzy = 0;
    if(l == r) {
        t[u].sum = 0;
        return;
    }
    ll mid = (l + r) >> 1;
    tbuild(lson);tbuild(rson);
    pushup(u);
}

void tupdate(ll u,ll l,ll r,ll a,ll b,ll c){
    if(a <= l && r <= b){
        t[u].sum += (r - l + 1) * c;
        t[u].sum %= p;
        t[u].lzy += c;
        return;
    }
    ll mid = (l + r) >> 1;
    pushdown(u,r - l + 1);
    if(a <= mid) tupdate(lson,a,b,c);
    if(mid < b) tupdate(rson,a,b,c);
    pushup(u);
}

ll tquery(ll u,ll l,ll r,ll a,ll b){
    if(a <= l && r <= b)	return t[u].sum;
    ll mid = (l + r) >> 1,ret = 0;
    pushdown(u,r - l + 1);
    if(a <= mid) ret += tquery(lson,a,b);
    if(mid < b) ret += tquery(rson,a,b);
    return ret % p;
}

void update(ll x,ll y,ll w){
    while(top[x] != top[y]){
        if(d[top[x]] < d[top[y]]) swap(x,y);
        tupdate(1,1,n,dfn[top[x]],dfn[x],w);
        x = f[top[x]];
    }
    if(d[x] < d[y]) swap(x,y);
    tupdate(1,1,n,dfn[y],dfn[x],w);
}

ll query(ll x,ll y){
    ll ret = 0;
    while(top[x] != top[y]){
        if(d[top[x]] < d[top[y]]) swap(x,y);
        ret += tquery(1,1,n,dfn[top[x]],dfn[x]);
        ret %= p;
        x = f[top[x]];
    }
    if(d[x] < d[y]) swap(x,y);
    ret += tquery(1,1,n,dfn[y],dfn[x]);
    ret %= p;
    return ret;	
}


struct Q1{
	ll p,flg,nm;
	bool operator < (const Q1 &b) const{
		return p < b.p;
	}
}a[maxn << 1];

struct Q{
	ll ans,z;
}q[maxn];

ll cnt;

ll x,y,z,op;

int main(){
//	freopen("a.txt","r",stdin);
	
	memset(head,-1,sizeof(head));
	n = read(); m = read();
	for(ll i = 2; i <= n; ++i){
		x = read() + 1;
		adde(x,i);
	}
	
	dfs1(1,0);dfs2(1,0);tbuild(1,1,n);
	
	for(ll i = 1; i <= m; ++i){
		a[++cnt].p = read();a[cnt].flg = 0;a[cnt].nm = i;
		a[++cnt].p = read() + 1;a[cnt].flg = 1;a[cnt].nm = i;
		q[i].z = read() + 1;
	}
	
	sort(a + 1,a + cnt + 1);
	cnt = 1;
	
	for(ll i = 0; i <= n; ++i){
		update(1,i,1);
		while(a[cnt].p == i){
			if(!a[cnt].flg) q[a[cnt].nm].ans -= query(1,q[a[cnt].nm].z);
			else q[a[cnt].nm].ans += query(1,q[a[cnt].nm].z);
			++cnt;
		}
	}
	
	for(ll i = 1; i <= m; ++i){
		printf("%lld\n",(q[i].ans + p) % p);
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值