【Luogu】P5901 [IOI2009] regions

文章讨论了一种针对树结构的查询优化方法,涉及在线和离线问题的处理。主要算法包括使用前缀和思想减少复杂度,以及根号分治策略来平衡不同计算方式的时间复杂度。代码实现包括DFS遍历和根据节点大小动态选择算法1或算法2。
摘要由CSDN通过智能技术生成

题目链接

点击打开链接

题目解法

这道题在线不好做,所以我们考虑把问题离线下来

考虑把询问挂在地区上

现在有两种计算的方式

  1. 把询问挂在 e1 上
    题意转化成询问在 e1 的子树中,有多少颜色是 r2 的
    看到子树不难想到转化为 dfn 序,但是这样需要一个二分的复杂度,会多出一个 log
    有一个比较好的做法是前缀和的思想
    我们可以在进入子树时把当前的 cnt 减掉,在出子树时把当前的 cnt 加上
  2. 把询问挂在 e2 上
    这没什么好说的,只要进入节点时加上,出去时减去,就满足是 r2 的祖先了

我们发现算法1和算法2各有好处

假如说 r2 的 size 为 S

算法1:不同的 r2 最多有 \frac{n}{S} 个,如果对每个不同的 r2 进行一遍 dfs,时间复杂度就是 O(\frac{n^{2}}{S})

算法2:q 次询问,假设 q 的大小与 n 同级,那么时间复杂度是 O(nS)

上面的时间复杂度分析后,算法呼之欲出:根号分治

如果 S>\sqrt{n} ,用算法1

否则用算法2

时间复杂度 O(n\sqrt{n})

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N(200100),M(25100),Q(200100);
int n,m,q,r[N],siz[M],cnt1[M],cnt2[M]; 
int Query[Q],ans[Q];
map<pair<int,int>,int> mp;
vector<pii> query1[M],query2[M];
vector<int> vec[N];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar())
		if(ch=='-')
			RR=-1;
	for(;isdigit(ch);ch=getchar())
		FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void dfs(int u){
	for(pii q2:query2[r[u]])
		ans[q2.second]+=cnt1[q2.first];
	for(pii q1:query1[r[u]])
		ans[q1.second]-=cnt2[q1.first];
	cnt1[r[u]]++,cnt2[r[u]]++;
	for(int v:vec[u])
		dfs(v);
	cnt1[r[u]]--;
	for(pii q1:query1[r[u]])
		ans[q1.second]+=cnt2[q1.first];
}
int main(){
	n=read(),m=read(),q=read();
	r[1]=read(),siz[r[1]]++;
	for(int i=2,x;i<=n;i++){
		x=read();vec[x].push_back(i);
		r[i]=read(),siz[r[i]]++;
	}
	int B=sqrt(n);
	for(int i=1,r1,r2;i<=q;i++){
		r1=read(),r2=read();
		if(!mp.count(make_pair(r1,r2))){
			mp[make_pair(r1,r2)]=i;
			if(siz[r1]<B)
				query1[r1].push_back(make_pair(r2,i));
			else
				query2[r2].push_back(make_pair(r1,i));
		}
		Query[i]=mp[make_pair(r1,r2)];
	}
	dfs(1);
	for(int i=1;i<=q;i++)
		printf("%d\n",ans[Query[i]]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值