2022杭电多校第二场 1001 Static Query on Tree(树链剖分+区间合并)

Static Query on Tree

思路:

在这里插入图片描述
求出集合 A A A和集合 B B B可达点的交集,集合 C C C的前驱节点的并集。两集合取交集即可。

题目保证所有节点可达 1 1 1号点。
A A A集合所有点到 1 1 1的路径和 B B B集合所有点到 1 1 1的路径的公共点即为两集合公共可达点。
C C C集合所有点的子树的并集即为所有前驱节点。 (可以自行画图)

对树的路径修改直接上树剖。

区间打标记最后查询同时存在三个标记的点即可。(2进制压位的做法如果最后整个区间去递归并最值优化 这个可以被卡)
三种区间每种区间合并,最后取并集即可。

Code:

#include <iostream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <vector>
#include <string>
#include <iomanip>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
//#include <unordered_map>
#define guo312 std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ll long long
#define Inf LONG_LONG_MAX
#define inf INT_MAX
#define endl "\n"
#define PI 3.1415926535898
using namespace std;
const int N=2e5+10;
vector<int> ve[N];
// 预处理
int dep[N],fa[N],son[N],_size[N];
void dfs1(int now,int last){
	dep[now]=dep[last]+1; fa[now]=last;
	int maxn=0; _size[now]=1;
	for(auto it:ve[now]){
		if(it==last) continue;
		dfs1(it,now);
		if(_size[it]>maxn){
			maxn=_size[it];
			son[now]=it;
		}
		_size[now]+=_size[it];
	}
}
// 树剖
int top[N],dfn[N],ti=0;
void dfs2(int now,int topf){
	dfn[now]=++ti,top[now]=topf;
	if(son[now]==0){
		return ;
	}
	dfs2(son[now],topf);
	for(auto it:ve[now]){
		if(it==son[now]||it==fa[now]) continue;
		dfs2(it,it);
	}
}
// 修改路径
void modify(int x,int y,vector< pair<int,int> > &pos){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]){
			swap(x,y);
		}
		pos.push_back({dfn[top[x]],dfn[x]});
		x=fa[top[x]];
	}
	if(dfn[x]>dfn[y]){
		swap(x,y);
	}
	pos.push_back({dfn[x],dfn[y]});
}
// 区间合并
void work(vector< pair<int,int> > &pos){
	sort(pos.begin(),pos.end());
	vector< pair<int,int> > res;
	int l=-inf,r=-inf;
	for(auto it:pos){
		if(r<it.first){
			if(l!=-inf){
				res.push_back({l,r});
			}
			l=it.first,r=it.second;
		}
		else{
			r=max(r,it.second);
		}
	}
	if(l!=-inf){
		res.push_back({l,r});
	}
	pos=res;
}
// 区间求交
void solve(vector< pair<int,int> > &A,vector< pair<int,int> > &B,vector< pair<int,int> > &C){
	int idxa=0,idxb=0,idxc=0,ans=0,l=0,r=0;
	int sa=A.size(),sb=B.size(),sc=C.size();
	while(idxa<sa&&idxb<sb&&idxc<sc){
		while(idxa<sa&&A[idxa].second<=l){
			idxa++;
		}
		while(idxb<sb&&B[idxb].second<=l){
			idxb++;
		}
		while(idxc<sc&&C[idxc].second<=l){
			idxc++;
		}
		if(idxa>=sa||idxb>=sb||idxc>=sc){
			break;
		}
		l=max({A[idxa].first,B[idxb].first,C[idxc].first});
		r=min({A[idxa].second,B[idxb].second,C[idxc].second});
		ans+=max(0,r-l+1);
		l=r;
	}
	cout<<ans<<endl;
}

int main(){
guo312;
	int t; cin>>t;
	while(t--){
		int n,q; cin>>n>>q;
		for(int i=2;i<=n;i++){
			ve[i].clear();
		}
		for(int i=2;i<=n;i++){
			int v; cin>>v;
			ve[i].push_back(v);
			ve[v].push_back(i);
		}
		dfs1(1,0); dfs2(1,1);
		while(q--){
			int sa,sb,sc; cin>>sa>>sb>>sc;
			vector< pair<int,int> > A;
			vector< pair<int,int> > B;
			vector< pair<int,int> > C;
			for(int i=1;i<=sa;i++){
				int pos; cin>>pos;
				modify(1,pos,A);
			}
			for(int i=1;i<=sb;i++){
				int pos; cin>>pos;
				modify(1,pos,B);
			}
			for(int i=1;i<=sc;i++){
				int pos; cin>>pos;
				C.push_back({dfn[pos],dfn[pos]+_size[pos]-1});
			}
			work(A),work(B),work(C);
			solve(A,B,C);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

要用bug来打败bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值