发现是一道比较裸的树上莫队,于是就开始刚,然后发现好像是最难的一道题……(本题解用于作者加深算法理解,也欢迎各位的阅读)

题意

给你一棵树,树有点权,询问一条路径上是否有点权为 \(c\) 的点。

题解

我们可以比较明显地发现询问是很像莫队的询问处理的,可以 \(O(1)\) 去扩展 \(l\) 和 \(r\) 。但是这题是树,所以我们需要引入欧拉序的概念。

欧拉序其实很像 \(dfs\) 序,但是会在出栈的时候多记录一次,我们可以利用欧拉序来将树上的路径转化为莫队需要的区间问题。

我们可以先画一张图:



其中位于节点右侧的是入栈时间,位于节点左侧的是出栈时间。

我们不妨以每一个点的入栈时间为编号,欧拉序则为:

\[1~2~3~4~4~6~6~3~9~9~11~11~2~14~15~16~16~15~19~20~20~19~14~1
\]

比如对于 \(9\) ~ \(16\) 这一条路径,我们可以用时间 \(10\) ~ \(16\) 来表示,其中出现两次的点我们不进行计算,并且还需要多加上 \(9\) 和 \(16\) 的 \(lca\):\(1\) ,这些可以用异或运算和特判来解决。即路径 \(x\) ~ \(y\):\(lst_x\) ~ \(fir_y\) 。

同时我们可以发现,如果路径上的点是为祖先关系,我们需要特殊处理,可以发现是 \(fir_x\) ~ \(fir_y\) 。

因此我们将所有的路径都转化为区间之后就可以用莫队离线实现了,复杂度 \(O(n\sqrt n)\) 。可是不开 \(O2\) 过不了……

以上。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=1e5+5;
int n,m,type[N];
struct Edge{int nxt,to;}e[N<<1];int head[N];
void add(int u,int v,int i){e[i]=Edge{head[u],v},head[u]=i;}
int fir[N],lst[N],dfn[N<<1],cnt_dfn=0;
int dep[N],fa[N][25];
void dfs(int u)
{
dfn[++cnt_dfn]=u,fir[u]=cnt_dfn;
for(int i=head[u];i;i=e[i].nxt)
{
if(e[i].to==fa[u][0]) continue;
fa[e[i].to][0]=u;
dep[e[i].to]=dep[u]+1;
dfs(e[i].to);
}
dfn[++cnt_dfn]=u,lst[u]=cnt_dfn;
}
int lca(int u,int v)
{
if(dep[u]>dep[v]) swap(u,v);
for(int i=20;i>=0;--i)
{
if(dep[fa[v][i]]>=dep[u])
v=fa[v][i];
}
if(u==v) return u;
for(int i=20;i>=0;--i)
{
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
}
return fa[u][0];
}
struct Query{int l,r,lca,c,id;}q[M];
int bel[N<<1],size;
bool cmp(Query a,Query b)
{
if(bel[a.l]^bel[b.l]) return bel[a.l]<bel[b.l];
if(bel[a.l]^1) return a.r<b.r;
return a.r>b.r;
}
int l=1,r=0,tag[N],cnt[N],ans[M];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;++i) scanf("%d",&type[i]);
for(int i=1,u,v;i<n;++i)
{
scanf("%d%d",&u,&v);
add(u,v,i<<1);
add(v,u,i<<1|1);
}
dep[1]=1,dfs(1);
for(int i=1;i<=20;++i)
{
for(int j=1;j<=n;++j)
fa[j][i]=fa[fa[j][i-1]][i-1];
}
size=sqrt(n*2);
for(int i=1,cnt=0;i<=n*2;i+=size)
{
++cnt;
for(int j=i;j<min(i+size,n*2+1);++j)
bel[j]=cnt;
}
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].c),q[i].id=i;
if(fir[q[i].l]>fir[q[i].r]) swap(q[i].l,q[i].r);
q[i].lca=lca(q[i].l,q[i].r);
if(q[i].lca==q[i].l) q[i].l=fir[q[i].l];
else q[i].l=lst[q[i].l];
q[i].r=fir[q[i].r];
}
// for(int i=1;i<=n*2;++i)
// printf("%d ",dfn[i]);
// printf("\n");
sort(q+1,q+1+m,cmp);
// for(int i=1;i<=m;++i)
// printf("%d %d %d\n",q[i].l,q[i].r,q[i].lca);
for(int i=1;i<=m;++i)
{
while(q[i].r>r)
{
tag[dfn[++r]]^=1;
if(tag[dfn[r]]) cnt[type[dfn[r]]]++;
else cnt[type[dfn[r]]]--;
}
while(q[i].r<r)
{
tag[dfn[r]]^=1;
if(tag[dfn[r]]) cnt[type[dfn[r--]]]++;
else cnt[type[dfn[r--]]]--;
}
while(q[i].l>l)
{
tag[dfn[l]]^=1;
if(tag[dfn[l]]) cnt[type[dfn[l++]]]++;
else cnt[type[dfn[l++]]]--;
}
while(q[i].l<l)
{
tag[dfn[--l]]^=1;
if(tag[dfn[l]]) cnt[type[dfn[l]]]++;
else cnt[type[dfn[l]]]--;
}
// printf("$$$%d %d\n",l,r);
// for(multiset<int>::iterator i=st.begin();i!=st.end();++i)
// printf("%d ",*i);
// printf("\n");
ans[q[i].id]=(cnt[q[i].c]||type[q[i].lca]==q[i].c);
}
for(int i=1;i<=m;++i) printf("%d",ans[i]);
printf("\n");
return 0;
}

P5838 [USACO19DEC]Milk Visits G的更多相关文章

  1. 【题解】[USACO19DEC]Milk Visits G

    题目戳我 \(\text{Solution:}\) 这题不要把思想局限到线段树上--这题大意就是求路径经过的值中\(x\)的出现性问题. 最开始的想法是值域线段树--看了题解发现直接\(vector\ ...

  2. P5836 [USACO19DEC]Milk Visits S 从并查集到LCA(最近公共祖先) Tarjan算法 (初级)

    为什么以它为例,因为这个最水,LCA唯一黄题. 首先做两道并查集的练习(估计已经忘光了).简单来说并查集就是认爸爸找爸爸的算法.先根据线索理认爸爸,然后查询阶段如果发现他们的爸爸相同,那就是联通一家的 ...

  3. 洛谷 P5837 [USACO19DEC]Milk Pumping G (单源最短路,dijkstra)

    题意:有一\(n\)个点,\(m\)条边的双向图,每条边都有花费和流量,求从\(1\)~\(n\)的路径中,求\(max\frac{min(f)}{\sum c}\). 题解:对于c,一定是单源最短路 ...

  4. P5837 [USACO19DEC]Milk Pumping G

    题目描述 Farmer John 最近为了扩张他的牛奶产业帝国而收购了一个新的农场.这一新的农场通过一个管道网络与附近的小镇相连,FJ 想要找出其中最合适的一组管道,将其购买并用来将牛奶从农场输送到小 ...

  5. Milk Pumping G&Milk Routing S 题解

    Milk Pumping G&Milk Routing S 双倍经验时间 洛谷P5837 [USACO19DEC]Milk Pumping G 洛谷P3063 [USACO12DEC]Milk ...

  6. 题解 P5837 【[USACO19DEC]Milk Pumping】

    这题其实想法挺简单的,因为他只需要简单的把每个点的花费和流量用dp记下来就好了 1.怎么记: 首先考虑dp的状态.由于所在的点和流量都要记,所以dp开二维,一维记所在的点,另一维记去哪 //dp[i] ...

  7. 2021record

    2021-10-14 P2577 [ZJOI2004]午餐 2021-10-13 CF815C Karen and Supermarket(小小紫题,可笑可笑) P6748 『MdOI R3』Fall ...

  8. USACO19DEC题解

    Bronze A Cow Gymnastics 题目:https://www.luogu.com.cn/problem/P5831 题解:用数组存一下出现位置,O(n^2)枚举一下就好. 代码: #i ...

  9. USACO 2019 December Contest 随记

    Forewords 今年 USACO 的比赛变化挺大的,有部分分了,而且不再是固定十个点了(部分分只说这几个点满足这几个性质,以为十个点的我还高兴了一会,一提交,...),除此之外居然赛后还排名了.这 ...

随机推荐

  1. 栈(Stack)和队列(Queue)是两种操作受限的线性表。

    (线性表:线性表是一种线性结构,它是一个含有n≥0个结点的有限序列,同一个线性表中的数据元素数据类型相同并且满足"一对一"的逻辑关系. "一对一"的逻辑关系指的 ...

  2. shell简介及变量的定义查看撤销

    1.shell分类及相关软件  图形界面Shell(Graphical User Interface shell 即 GUI shell),如:GNOME.KDE 命令行式Shell(Command ...

  3. 《JavaScript高级程序设计》读书笔记 ---继承

    继承是OO 语言中的一个最为人津津乐道的概念.许多OO 语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.如前所述,由于函数没有签名,在ECMAScrip ...

  4. Java8 方法引用和构造方法引用

    如果不熟悉Java8新特性的小伙伴,初次看到函数式接口写出的代码可能会是一种懵逼的状态,我是谁,我在哪,我可能学了假的Java,(・∀・(・∀・(・∀・*),但是语言都是在进步的,就好比面向对象的语言 ...

  5. Mac插件太多太乱怎么办?CleanMyMac直接帮你搞定!

    电脑应用插件在一定程度上便利了大家的生活,保障了用户的使用安全,比如Flash插件.浏览器翻译插件.银行安全登录插件等等.但是许多的插件并不能定位安装的位置,同时部分插件,大部分时候都是只使用一次的, ...

  6. 考研党其实可以用思维导图MindManager做考研复习计划

    近年由于就业压力,个人学历提升等各种原因,考研的人数越来越多了,相对难度也越来越大了,尽管今年研究生招生规模同比去年增加18.9万,但也无法掩盖考研的竞争逐年激烈. 身为考研大军中的预备选手之一,小编 ...

  7. 下载器Folx专业版有没有iTunes整合功能

    对于使用Mac系统的用户来说,相信对iTunes都不陌生.Folx专业版提供的iTunes整合功能,能将下载的音频.电影等文件自动同步到iTunes. 该功能将会有助于用户的音频.视频整合,对于喜欢听 ...

  8. 为什么Java不允许创建范型数组

    问题示例 List<Integer>[] intListArr = new ArrayList<Integer>[8]; // 编译时报错 能看到这么看似没啥问题的一个简单语句 ...

  9. Invalid operation updata 错误解决方法

    错误: huny@DESKTOP-N1EBKQP:/mnt/c/Users/Administrator$ sudo apt-get updata E: Invalid operation updata ...

  10. SPG-Net: Segmentation Prediction and Guidance Network for Image Inpainting

    SPG-Net: Segmentation Prediction and Guidance Network for Image Inpainting pytorch 引言 利用语义分割获取空洞的边缘信 ...