首页 > 其他分享 >P5838 [USACO19DEC]Milk Visits G 【树上莫队】

P5838 [USACO19DEC]Milk Visits G 【树上莫队】

时间:2022-08-24 23:56:24浏览次数:86  
标签:USACO19DEC int work Visits dfs lca 莫队 P5838

P5838 [USACO19DEC]Milk Visits G

给定一颗树,每个点有点权,\(m\) 次询问,每次求 \(u\) 到 \(v\) 之间的路径是否出现过权值为 \(w\) 的点。


树上莫队板子题,我们需要一个 dfs 序来帮助进行莫队操作,dfs 序记录一个 \(in\) 一个 \(out\),所以我们有 \(2 \times n\) 的 dfs 序,每次读入时先将 \(in\) 的大小选出 \(l,r\) ,求出 \(lca(l,r)\) 。

然后分两种情况讨论:如果 \(l\) 是 \(r\) 的 \(lca\) (包含 \(l=r\)),则标记 \(in[l],in[r],lca=-1\) ;不然则认为是一条普通路径,标记 \(out[l],in[r],lca=lca(l,r)\) 。这样操作保证 \([l,r]\) 中间的点只会被遍历一次,如果 \(lca != -1\) ,则需要指针移动完毕时加入 \(lca\) 再记录答案,然后删除 \(lca\) 的贡献。

因为 dfs 序的关系,不同与普通序列,我们额外需要为莫队指针使用一个 \(vis\) 数组记录当前的点是否出现,出现则加入,否则删除,每次 \(vis[u] xor = 1\)。区别与普通莫队的 add,del 。树上莫队只需要一个 \(work\) 就可以同时实现两个操作了。

Code.

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,h[N],ne[N<<1],e[N<<1],idx,len,cnt,st[N],vis[N],sz[N],w[N],in[N],out[N],top[N],son[N],fa[N],dep[N],pl[N<<1],ans[N];
inline int get(int x) {return x/len;}
struct node
{
	int l,r,w,lca,id;
	bool operator <(const node &o) const {
		int i=get(l),j=get(o.l);
		if(i == j) return r < o.r; return i < j;
	} 
} q[N];
void add(int u,int v) {ne[++idx]=h[u],e[idx]=v,h[u]=idx;}
void dfs1(int u,int father,int depth)
{
	in[u]=++cnt; pl[cnt]=u; fa[u]=father; dep[u]=depth; sz[u]=1;
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i]; if(j == father) continue ;
		dfs1(j,u,depth+1); sz[u]+=sz[j];
		if(sz[son[u]] < sz[j]) son[u]=j;
	}
	out[u]=++cnt; pl[cnt]=u;
}
void dfs2(int u,int t)
{
	top[u]=t; if(! son[u]) return ; dfs2(son[u],t);
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j == fa[u] || j == son[u]) continue ;
		dfs2(j,j);
	}
}
inline int lca(int u,int v)
{
	while(top[u] != top[v])
	{
		if(dep[top[u]] < dep[top[v]]) swap(u,v);
		u=fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}
inline void work(int x) {if(vis[pl[x]] == 0) st[w[pl[x]]]++,vis[pl[x]]=1; else st[w[pl[x]]]--,vis[pl[x]]=0; }
int main()
{
	memset(h,-1,sizeof h); scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
	for(int i=1,u,v;i<n;i++) {scanf("%d%d",&u,&v); add(u,v); add(v,u);}
	len=(int)sqrt(2*n)+1; dfs1(1,0,1); dfs2(1,1);
	for(int i=1,u,v;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&q[i].w); q[i].id=i; if(in[u] > in[v]) swap(u,v); 
		if(u == v) {q[i].l=in[u],q[i].r=in[v],q[i].lca=-1; continue ;} 
		int yl=lca(u,v);
		if(yl == u) q[i].l=in[u],q[i].r=in[v],q[i].lca=-1;
		else q[i].l=out[u],q[i].r=in[v],q[i].lca=yl;
	}
	stable_sort(q+1,q+1+m);
	int l=1,r=0;
	for(int i=1;i<=m;i++)
	{
		int l1=q[i].l,r1=q[i].r;
		while(l < l1) work(l++);
		while(l > l1) work(--l);
		while(r < r1) work(++r);
		while(r > r1) work(r--);
		if(q[i].lca != -1) work(in[q[i].lca]);
		ans[q[i].id]=st[q[i].w];
		if(q[i].lca != -1) work(in[q[i].lca]);
	}
	for(int i=1;i<=m;i++) if(ans[i] == 0) cout<<0; else cout<<1;
	return 0;
}

标签:USACO19DEC,int,work,Visits,dfs,lca,莫队,P5838
From: https://www.cnblogs.com/EastPorridge/p/16622728.html

相关文章

  • 带修莫队例题详解
    带修莫队[P1903国家集训队]数颜色/维护队列版本更新内容:在普通莫队基础上增加时间坐标,提高游戏难度;排序时以时间坐标为第三关键字,奇偶排序玄学值上调\(20\%\);代......
  • dfs序 括号序 欧拉序 树上莫队 虚树建立 傻傻分不清
    括号序进加一次,出加一次,显然最后得到的序列只有\(2n\)个点。voiddfs(intx){ in[x]=++tot; for(y)dfs(y); out[x]=++tot;}显然我们希求将树上路径表示为区......