题目链接

题意:给出一棵树,有边权,\(m\) 次询问,每次给出三个数 \(p,l,r\),求边集 \(\bigcap\limits_{i=l}^rE(p,i)\) 中所有边的权值和。

其中 \(E(u,v)\) 为点 \(u\) 到点 \(v\) 的路径中经过的边的集合。

强制在线。

\(1 \leq n \leq 2 \times 10^5\)。

果然是神仙 ix35 wdl 出的题啊……

不妨以 \(1\) 为根,做一遍 dfs 求出它们的 dfs 序,以及它们到根的距离 \(d_i\)。

每次询问,我们求出 \(l,l+1,\dots,r\) 中所有点的 LCA,假设为 \(t\)。

我们将 \([l,r]\) 中的点分成三个集合:

  • 集合 A:在 \(p\) 的子树中。
  • 集合 B:不在 \(p\) 的子树中,但是与 \(p\) 在根节点的同一子树中。
  • 集合 C:与 \(p\) 在根节点的不同子树中。

    接下来就是分情况讨论了:
  1. 如果所有点都属于集合 A,那么答案就是 \(d_t-d_p\)
  2. 如果有的点都属于集合 A,有的点不属于集合 A,那么答案为 \(0\)。
  3. 如果没有点属于集合 A,那么从 \(p\) 不断地往上跳,直到 \([l,r]\) 中至少有一个点在当前点的子树内,记当前所在的点为 \(f\),那么又有两种情况:
  • \(f\) 在 \(t\) 上方,那么答案为 \(d_p+d_t-2 \times d_f\)
  • \(f\) 在 \(t\) 下方,那么答案为 \(d_p-d_f\)

    口胡完了,实现也不难:
  • 求区间 LCA,建一棵线段树,节点上维护 LCA。
  • 判断有多少个点在 \(x\) 的子树中:注意到 \(x\) 子树的 dfs 序是一个连续的区间,可以想到主席树。以 dfs 序为节点下标建一棵主席树。第 \(i\) 棵树的第 \(j\) 个位置上的数表示节点 \(1\) 到节点 \(i\) 中有多少个点的 dfs 序为 \(j\)。
  • 往上跳:倍增
//Coded by tzc_wk
/*
数据不清空,爆零两行泪。
多测不读完,爆零两行泪。
边界不特判,爆零两行泪。
贪心不证明,爆零两行泪。
D P 顺序错,爆零两行泪。
大小少等号,爆零两行泪。
变量不统一,爆零两行泪。
越界不判断,爆零两行泪。
调试不注释,爆零两行泪。
溢出不 l l,爆零两行泪。
忘文件操作,爆零两行泪。
*/
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define foreach(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define all(a) a.begin(),a.end()
#define giveup(...) return printf(__VA_ARGS__),0;
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,0x3f,sizeof(a))
#define fillsmall(a) memset(a,0xcf,sizeof(a))
#define mask(a) (1ll<<(a))
#define maskx(a,x) ((a)<<(x))
#define _bit(a,x) (((a)>>(x))&1)
#define _sz(a) ((int)(a).size())
#define filei(a) freopen(a,"r",stdin);
#define fileo(a) freopen(a,"w",stdout);
#define fileio(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
#define eprintf(...) fprintf(stderr,__VA_ARGS__)
#define put(x) putchar(x)
#define eoln put('\n')
#define space put(' ')
#define y1 y_chenxiaoyan_1
#define y0 y_chenxiaoyan_0
//#define int long long
typedef pair<int,int> pii;
inline int read(){
int x=0,neg=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') neg=-1;
c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*neg;
}
inline void print(int x){
if(x<0){
putchar('-');
print(abs(x));
return;
}
if(x<=9) putchar(x+'0');
else{
print(x/10);
putchar(x%10+'0');
}
}
inline int qpow(int x,int e,int _MOD){
int ans=1;
while(e){
if(e&1) ans=ans*x%_MOD;
x=x*x%_MOD;
e>>=1;
}
return ans;
}
int n=read(),m=read();
struct edge{
int u,v,w;
edge(){/*Problemsetter of P6071 AK IOI*/}
edge(int _u,int _v,int _w){
u=_u;v=_v;w=_w;
}
};
vector<edge> g[200005];
int idx,dist[200005],dep[200005],dfn[200005],fa[200005][23],sz[200005],id[200005];
inline void dfs(int x,int f){
dfn[x]=++idx;
id[idx]=x;
fa[x][0]=f;
sz[x]=1;
foreach(it,g[x]){
int y=it->v,z=it->w;
if(y==f) continue;
dist[y]=dist[x]+z;
dep[y]=dep[x]+1;
dfs(y,x);
sz[x]+=sz[y];
}
}
inline int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=22;i>=0;i--){
if(dep[x]-(1<<i)>=dep[y]) x=fa[x][i];
}
if(x==y) return x;
for(int i=22;i>=0;i--){
if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
}
return fa[x][0];
}
struct SegTree{
struct node{
int l,r,lca;
} s[200005<<2];
inline void build(int k,int l,int r){
s[k].l=l;s[k].r=r;
if(l==r){
s[k].lca=l;
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
s[k].lca=LCA(s[k<<1].lca,s[k<<1|1].lca);
}
inline int query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r){
return s[k].lca;
}
int mid=(s[k].l+s[k].r)>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return LCA(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
} segt;
struct hjtree{
struct node{
int l,r,ch[2],cnt;
} s[200005<<5];
int ncnt=0,rt[200005];
inline void build(int &k,int l,int r){
k=++ncnt;s[k].l=l;s[k].r=r;s[k].cnt=0;
if(l==r) return;
int mid=(l+r)>>1;
build(s[k].ch[0],l,mid);
build(s[k].ch[1],mid+1,r);
}
inline void modify(int &k,int pre,int x){
k=++ncnt;s[k]=s[pre];s[k].cnt++;
if(s[k].l==s[k].r) return;
int mid=(s[k].l+s[k].r)>>1;
if(x<=mid) modify(s[k].ch[0],s[pre].ch[0],x);
else modify(s[k].ch[1],s[pre].ch[1],x);
s[k].cnt=s[s[k].ch[0]].cnt+s[s[k].ch[1]].cnt;
}
inline int query(int k,int l,int r){
// cout<<s[k].l<<" "<<s[k].r<<" "<<s[k].cnt<<endl;
if(l<=s[k].l&&s[k].r<=r) return s[k].cnt;
int mid=(s[k].l+s[k].r)>>1;
if(r<=mid) return query(s[k].ch[0],l,r);
else if(l>mid) return query(s[k].ch[1],l,r);
else return query(s[k].ch[0],l,mid)+query(s[k].ch[1],mid+1,r);
}
} hjt;
inline int getcnt(int p,int l,int r){
// cout<<hjt.rt[0]<<" "<<hjt.rt[l-1]<<endl;
// cout<<hjt.query(hjt.rt[l-1],dfn[p],dfn[p]+sz[p]-1)<<endl;
return hjt.query(hjt.rt[r],dfn[p],dfn[p]+sz[p]-1)-hjt.query(hjt.rt[l-1],dfn[p],dfn[p]+sz[p]-1);
}
signed main(){
fz(i,1,n-1){
int u=read(),v=read(),w=read();
g[u].push_back(edge(u,v,w));
g[v].push_back(edge(v,u,w));
}
dfs(1,0);
// fz(i,1,n) cout<<dfn[i]<<endl;
fz(i,1,22) fz(j,1,n) fa[j][i]=fa[fa[j][i-1]][i-1];
segt.build(1,1,n);
hjt.build(hjt.rt[0],1,n);
fz(i,1,n) hjt.modify(hjt.rt[i],hjt.rt[i-1],dfn[i]);
int ans=0;
// cout<<LCA(2,5)<<endl;
while(m--){
int p=read(),l=read(),r=read();
p^=ans;l^=ans;r^=ans;
int lc=segt.query(1,l,r);
int num=getcnt(p,l,r);
// cout<<"lc="<<lc<<"\tnum="<<num<<endl;
if(num==r-l+1) ans=dist[lc]-dist[p];
else if(num>0) ans=0;
else{
int cur=p;
for(int i=20;i>=0;i--){
if(fa[cur][i]&&getcnt(fa[cur][i],l,r)==0)
cur=fa[cur][i];
}
cur=fa[cur][0];
// cout<<"cur="<<cur<<endl;
if(dist[cur]<dist[lc]) ans=dist[p]+dist[lc]-(dist[cur]<<1);
else ans=dist[p]-dist[cur];
}
printf("%d\n",ans);
}
return 0;
}

洛谷 P6071 『MdOI R1』Treequery(LCA+线段树+主席树)的更多相关文章

  1. 洛谷 P6072 -『MdOI R1』Path(回滚莫队+01-trie)

    题面传送门 又是 ix35 神仙出的题,先以 mol 为敬 %%% 首先预处理出根节点到每个点路径上权值的异或和 \(dis_i\),那么两点 \(a,b\) 路径上权值的异或和显然为 \(dis_a ...

  2. 洛谷 P6383 -『MdOI R2』Resurrection(DP)

    洛谷题面传送门 高速公路上正是补 blog 的时候,难道不是吗/doge,难不成逆在高速公路上写题/jy 首先形成的图显然是连通图并且有 \(n-1\) 条边.故形成的图是一棵树. 我们考虑什么样的树 ...

  3. 『MdOI R1』Treequery

    我们可以思考怎么做呢. 首先我们需要进行一些分类讨论: 我们先思考一下如果所有关键点都在 \(p\) 的子树内, 那显然是所有关键点的 \(Lca\) 到 \(p\) 距离. 如果所有关键点一些在 \ ...

  4. P6072 『MdOI R1』Path

    考虑我们有这样操作. 我们只要维护两点在子树内和两点在子树外的异或和即可. 前者可以类似于线段树合并的trie树合并. 后者有两种做法: 一种是把dfn序翻倍:然后子树补变成了一个区间最大异或问题,可 ...

  5. 洛谷P3402 【模板】可持久化并查集 [主席树,并查集]

    题目传送门 可持久化并查集 n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 ...

  6. 洛谷3月月赛 R1 Step! ZERO to ONE

    洛谷3月月赛 R1 Step! ZERO to ONE 普及组难度 290.25/310滚粗 t1 10分的日语翻译题....太难了不会... t2 真·普及组.略 注意长为1的情况 #include ...

  7. 「洛谷4197」「BZOJ3545」peak【线段树合并】

    题目链接 [洛谷] [BZOJ]没有权限号嘤嘤嘤.题号:3545 题解 窝不会克鲁斯卡尔重构树怎么办??? 可以离线乱搞. 我们将所有的操作全都存下来. 为了解决小于等于\(x\)的操作,那么我们按照 ...

  8. 「洛谷3870」「TJOI2009」开关【线段树】

    题目链接 [洛谷] 题解 来做一下水题来掩饰ZJOI2019考炸的心情QwQ. 很明显可以线段树. 维护两个值,\(Lazy\)懒标记表示当前区间是否需要翻转,\(s\)表示区间还有多少灯是亮着的. ...

  9. 洛谷P1395 会议(CODEVS.3029.设置位置)(求树的重心)

    To 洛谷.1395 会议 To CODEVS.3029 设置位置 题目描述 有一个村庄居住着n个村民,有n-1条路径使得这n个村民的家联通,每条路径的长度都为1.现在村长希望在某个村民家中召开一场会 ...

随机推荐

  1. Pytorch——张量 Tensors

    张量 Tensors 1.torch.is_tensor torch.is_tensor(obj) 用法:判断是否为张量,如果是 pytorch 张量,则返回 True. 参数:obj (Object ...

  2. javascript高级程序设计第三版书摘

    在HTML 中使用JavaScript <script>元素 在使用<script>元素嵌入 JavaScript 代码时,只须为<script>指定 type 属 ...

  3. 分享一份软件测试项目实战(web+app+h5+小程序)

    大家好,我是谭叔. 本次,谭叔再度出马,给大家找了一个非常适合练手的软件测试项目,此项目涵盖web端.app端.h5端.小程序端,可以说非常之全面. 缘起 在这之前,谭叔已经推出了九套实战教程. 但是 ...

  4. kivy 选择框

    from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.lang import builder # 注册 ...

  5. Intellij IDEA 2021.2.3 最新版免费激活教程(可激活至 2099 年,亲测有效)

    ​ 申明,本教程 Intellij IDEA 最新版破解.激活码均收集与网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除.如条件允许,建议大家购买正版. 本教程更新于:2021 年 10 月 ...

  6. Spring DeferredResult 异步请求

    Spring DeferredResult 异步请求 一.背景 二.分析 三.实现要求 四.后端代码实现 五.运行结果 1.超时操作 2.正常操作 六.DeferredResult运行原理 六.注意事 ...

  7. filebeat收集日志到elsticsearch中并使用ingest node的pipeline处理

    filebeat收集日志到elsticsearch中 一.需求 二.实现 1.filebeat.yml 配置文件的编写 2.创建自定义的索引模板 3.加密连接到es用户的密码 1.创建keystore ...

  8. Python ImportError: cannot import name ABC

    Python 3.5.2 测试可以运行 import sys from abc import ABC,abstractmethod class MyBase(ABC): @abstractmethod ...

  9. Openeuler安装完整man手册

    Openeuler安装完整man手册 ​ 在 Debian 和 Ubuntu 中安装了Shell 前端软件包管理器apt(Advanced Packaging Tool),可以通过如下方式安装. ​ ...

  10. win10 vscode安装babel

    第一步:安装 babel-cli cd进入项目根目录,执行命令: npm install --global babel-cli 第二步:检测第一步是否成功,输入命令 babel --version,若 ...