题目链接
题目解法
这道题在线不好做,所以我们考虑把问题离线下来
考虑把询问挂在地区上
现在有两种计算的方式
- 把询问挂在 e1 上
题意转化成询问在 e1 的子树中,有多少颜色是 r2 的
看到子树不难想到转化为 dfn 序,但是这样需要一个二分的复杂度,会多出一个 log
有一个比较好的做法是前缀和的思想
我们可以在进入子树时把当前的 cnt 减掉,在出子树时把当前的 cnt 加上 - 把询问挂在 e2 上
这没什么好说的,只要进入节点时加上,出去时减去,就满足是 r2 的祖先了
我们发现算法1和算法2各有好处
假如说 r2 的 size 为 S
算法1:不同的 r2 最多有 个,如果对每个不同的 r2 进行一遍 dfs,时间复杂度就是
算法2:q 次询问,假设 q 的大小与 n 同级,那么时间复杂度是
上面的时间复杂度分析后,算法呼之欲出:根号分治
如果 ,用算法1
否则用算法2
时间复杂度
#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;
}