CF536E Tavas on the Path 题解

CF536E Tavas on the Path 题解

一道小清新树剖题。。。

感觉这道题和P2486 [SDOI2011] 染色比较像,都是求颜色段。

题意简述

给定一棵 \(n\) 个节点的树,每条边有边权。

\(m\) 个询问,形式为 \((u,v,l)\),求 \(u\)\(v\) 的路径,假设长度为 \(p\),第 \(i\) 条边权值为 \(x_i\),构造一个长度为 \(p\)\(01\)\(s\),如果 \(x_i\ge l\),那么 \(s_i=1\),否则 \(s_i=0\)

对于得到的串 \(s\),假设它有 \(k\) 段连续的 1,第 \(i\) 段长度为 \(p_i\),那么要你输出所有 \(f_{p_i}\) 的和,其中 \(f\) 数组一开始就给出。

题解

我们设初始所有边为 \(0\)。首先把询问离线,按 \(l\) 从大到小排序,然后每到一个询问,就把所有大于等于这个询问的 \(l\) 的边设为 \(1\),然后询问从 \(u\)\(v\) 的答案。因为排了一遍序,所以每条边至多一次被设为 \(1\)

接着考虑如何处理询问,如果这是一个序列的话,我们可以用线段树来维护。

具体来说,每个节点设四个变量 \(x,l,r,b\),分别表示这个区间的答案,左边 \(1\) 的个数,右边 \(1\) 的个数,整个区间是否全为 \(1\)。根据以上定义,我们可以写出两个区间合并的代码:

struct node
{
    int x,l,r;bool b;
    friend node operator +(node x,node y)
    {return {x.x+y.x-f[x.r]-f[y.l]+f[x.r+y.l],x.l+x.b*y.l,y.r+y.b*x.r,x.b&&y.b};}
}t[N << 2];

考虑把序列问题转化为树上问题,在代码中加一个树剖即可。

但是要注意的是,这个合并是不满足交换律的,所以不能像一般树剖一样做。比如在求 \(x-y\) 这条路径的答案时,我们应该记两个变量 \(ans1\)\(ans2\),表示 \(x\) 向上跳的答案,和 \(y\) 向上跳的答案。当 swap(x,y) 时,同时也要 swap(ans1,ans2)。最后合并的时候,因为 \(ans1\)\(ans2\) 表示的是 \(x\)\(y\) 向上跳的答案,不能直接加,需要先 swap(x.l,x.r),再返回 \(ans1+ans2\)

另外,因为这道题是将边权下放到点权,所以要注意路径不包括 LCA,求答案时需要特判一下。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls x<<1
#define rs x<<1|1
#define lson x<<1,l,mid
#define rson x<<1|1,mid+1,r
#define ll long long
using namespace std;
const int N = 1e5+5;
int ans[N],f[N],hd[N],cnt,n,m;
struct ques{int u,v,w,id;}a[N],q[N];
bool cmp(ques x,ques y){return x.w > y.w;}
struct edge{int to,nex;}e[N << 1];
void add(int u,int v)
{e[++cnt] = {v,hd[u]};hd[u] = cnt;}
int fa[N],siz[N],d[N],son[N],top[N],rk[N],id[N],tim;
struct node
{
    int x,l,r;bool b;
    friend node operator +(node x,node y)
    {return {x.x+y.x-f[x.r]-f[y.l]+f[x.r+y.l],x.l+x.b*y.l,y.r+y.b*x.r,x.b&&y.b};}
}t[N << 2];
void modify(int x,int l,int r,int i)
{
    if(l == r){t[x] = {f[1],1,1,1};return ;}
    int mid = l+r>>1;
    i <= mid?modify(lson,i):modify(rson,i);
    t[x] = t[ls]+t[rs];
}
node query(int x,int l,int r,int L,int R)
{
    if(L <= l&&r <= R)return t[x];
    int mid = l+r>>1;node ans = {0,0,0,1};
    if(L <= mid)ans = query(lson,L,R);
    if(mid < R)ans = ans+query(rson,L,R);
    return ans;
}
void dfs1(int u,int f)
{
    fa[u] = f;d[u] = d[f]+1;siz[u] = 1;
    for(int i = hd[u],v;i;i = e[i].nex)
    {
        if((v = e[i].to) == f)continue;
        dfs1(v,u);siz[u] += siz[v];
        if(siz[son[u]] < siz[v])son[u] = v;
    }
}
void dfs2(int u,int tp)
{
	id[u] = ++tim;rk[tim] = u;top[u] = tp;
	if(son[u])dfs2(son[u],tp);
	for(int i = hd[u],v;i;i = e[i].nex)
		if((v = e[i].to) != fa[u]&&v != son[u])dfs2(v,v);
}
node queryRange(int x,int y)
{
    node ans1 = {0,0,0,1},ans2 = {0,0,0,1};
    while(top[x] != top[y])
    {
        if(d[top[x]] < d[top[y]])swap(x,y),swap(ans1,ans2);
        ans1 = query(1,1,n,id[top[x]],id[x])+ans1;
        x = fa[top[x]];
    }
    if(d[x] > d[y])swap(x,y),swap(ans1,ans2);
    if(id[x] != id[y])ans2 = query(1,1,n,id[x]+1,id[y])+ans2;
    return swap(ans1.l,ans1.r),ans1+ans2;
}
inline int rd()
{
    char c;int f = 1;
    while(!isdigit(c = getchar()))if(c=='-')f = -1;
    int x = c-'0';
    while(isdigit(c = getchar()))x = x*10+(c^48);
    return x*f;
}
int main()
{
    n = rd();m = rd();
    for(int i = 1;i < n;i++)f[i] = rd();
    for(int i = 1;i < n;i++)
    {
        int u = rd(),v = rd(),w = rd();
        a[i] = {u,v,w,0};add(u,v);add(v,u);
    }
    dfs1(1,0);dfs2(1,1);
    for(int i = 1;i <= n;i++)
        if(d[a[i].u] > d[a[i].v])swap(a[i].u,a[i].v);
    for(int i = 1;i <= m;i++)q[i] = {rd(),rd(),rd(),i};
    sort(a+1,a+n+1,cmp);sort(q+1,q+m+1,cmp);
    for(int i = 1,j = 1;i <= m;i++)
    {
        for(;j < n&&a[j].w >= q[i].w;j++)
            modify(1,1,n,id[a[j].v]);
        ans[q[i].id] = queryRange(q[i].u,q[i].v).x;
    }
    for(int i = 1;i <= m;i++)
        printf("%d\n",ans[i]);
    return 0;
}
posted @ 2024-07-29 10:18  max0810  阅读(44)  评论(0)    收藏  举报