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;
}

浙公网安备 33010602011771号