P5838 [USACO19DEC]Milk Visits G
发现是一道比较裸的树上莫队,于是就开始刚,然后发现好像是最难的一道题……(本题解用于作者加深算法理解,也欢迎各位的阅读)
题意
给你一棵树,树有点权,询问一条路径上是否有点权为 \(c\) 的点。
题解
我们可以比较明显地发现询问是很像莫队的询问处理的,可以 \(O(1)\) 去扩展 \(l\) 和 \(r\) 。但是这题是树,所以我们需要引入欧拉序的概念。
欧拉序其实很像 \(dfs\) 序,但是会在出栈的时候多记录一次,我们可以利用欧拉序来将树上的路径转化为莫队需要的区间问题。
我们可以先画一张图:

其中位于节点右侧的是入栈时间,位于节点左侧的是出栈时间。
我们不妨以每一个点的入栈时间为编号,欧拉序则为:
\]
比如对于 \(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的更多相关文章
- 【题解】[USACO19DEC]Milk Visits G
题目戳我 \(\text{Solution:}\) 这题不要把思想局限到线段树上--这题大意就是求路径经过的值中\(x\)的出现性问题. 最开始的想法是值域线段树--看了题解发现直接\(vector\ ...
- P5836 [USACO19DEC]Milk Visits S 从并查集到LCA(最近公共祖先) Tarjan算法 (初级)
为什么以它为例,因为这个最水,LCA唯一黄题. 首先做两道并查集的练习(估计已经忘光了).简单来说并查集就是认爸爸找爸爸的算法.先根据线索理认爸爸,然后查询阶段如果发现他们的爸爸相同,那就是联通一家的 ...
- 洛谷 P5837 [USACO19DEC]Milk Pumping G (单源最短路,dijkstra)
题意:有一\(n\)个点,\(m\)条边的双向图,每条边都有花费和流量,求从\(1\)~\(n\)的路径中,求\(max\frac{min(f)}{\sum c}\). 题解:对于c,一定是单源最短路 ...
- P5837 [USACO19DEC]Milk Pumping G
题目描述 Farmer John 最近为了扩张他的牛奶产业帝国而收购了一个新的农场.这一新的农场通过一个管道网络与附近的小镇相连,FJ 想要找出其中最合适的一组管道,将其购买并用来将牛奶从农场输送到小 ...
- Milk Pumping G&Milk Routing S 题解
Milk Pumping G&Milk Routing S 双倍经验时间 洛谷P5837 [USACO19DEC]Milk Pumping G 洛谷P3063 [USACO12DEC]Milk ...
- 题解 P5837 【[USACO19DEC]Milk Pumping】
这题其实想法挺简单的,因为他只需要简单的把每个点的花费和流量用dp记下来就好了 1.怎么记: 首先考虑dp的状态.由于所在的点和流量都要记,所以dp开二维,一维记所在的点,另一维记去哪 //dp[i] ...
- 2021record
2021-10-14 P2577 [ZJOI2004]午餐 2021-10-13 CF815C Karen and Supermarket(小小紫题,可笑可笑) P6748 『MdOI R3』Fall ...
- USACO19DEC题解
Bronze A Cow Gymnastics 题目:https://www.luogu.com.cn/problem/P5831 题解:用数组存一下出现位置,O(n^2)枚举一下就好. 代码: #i ...
- USACO 2019 December Contest 随记
Forewords 今年 USACO 的比赛变化挺大的,有部分分了,而且不再是固定十个点了(部分分只说这几个点满足这几个性质,以为十个点的我还高兴了一会,一提交,...),除此之外居然赛后还排名了.这 ...
随机推荐
- 四、c++总结------linux多线程服务端编程
- Spring Cloud Gateway原理
1.使用 compile 'org.springframework.cloud:spring-cloud-starter-gateway' 2.包结构 actuate中定义了一个叫GatewayCon ...
- Pinpoint 编译环境搭建(Pinpoint系列一)
本文基于 Pinpoint 2.1.0 版本 目录 一.2.1.0 版本特性 二.编译环境准备 三.编译注意事项 四.编译目录 五.注意事项 新版本的内容参考官方文档, Pinpoint的整个搭建是历 ...
- 在Linux中输入命令时打错并按了enter
今天在Linux中输入命令时,打错一个单词了,之后出现一串串的~,按ESC也没用, 并在底部出现:quit<enter> to exit vim 解决办法: 按几下 esc 确保 vim ...
- JWT(JSON Web Token)入门
简介 JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案 一.跨域认证的问题 互联网服务离不开用户认证.一般流程是下面这样. 1.用户向服务器发送用户名和密码. 2.服务器验证 ...
- Codeforces375D Tree and Queries
dsu on tree 题目链接 点我跳转 题目大意 给定一棵 \(n\) 个节点的树,根节点为 \(1\).每个节点上有一个颜色 \(c_i\) \(m\) 次询问. 每次询问给出 \(u\) \( ...
- 攻克solo第七课(Randy Rhoads风格)
本期文章,笔者将通过Guitar Pro 7吉他软件跟大家分享一下Randy Rhoads的solo句子. 相信很多精研电吉他的朋友都会听过这个一手把Ozzy Osbourne从离开黑色安息日乐队的深 ...
- 怎么在word里编辑插入数学公式?
大学时代我们都有一个共同的噩梦--高数.每次上完高数课都有一些数学公式.可是我们最难的就是想用电脑在word中做笔记的时候该怎样用word插入公式.Word中自有的公式太少,新公式又太难输入.这也是一 ...
- 如何在MathType输入手写体a
作为强大的数学公式编辑器,MathType中还能设置各种样式,还支持自定义设置,给大家编辑公式提供了更多的便利.那么有用户问:要如何将输入的字母a变为手写体呢?下面就来一起学习. 输入手写体a的步骤如 ...
- jQuery 第四章 实例方法 DOM操作_基于jQuery对象增删改查相关方法
.next() .prev() .nextAll() .prevAll() .prevUntil() .nextUntli() .siblings() .children() .parent() .p ...