【20190125 颜色对】

【20190125 颜色对】

WOJ地址

每对 ( a , b ) (a,b) ab ( u , v ) (u,v) uv保证 u u u v v v的祖先节点,就先想到了查询 u u u的子树,用上 D F S DFS DFS序,就可以转为区间查询了。

如果在线查,建 r r r颗树状数组,或是每次重新建,或直接暴力,肯定是不可取的,就考虑离线查。

初步考虑对于每一个颜色单独查询。

首先先把节点的颜色,询问的 b b b从大到小排序。(每个颜色可以对应查询)
再跑 D F S DFS DFS序。

按照询问一个一个地查。
如果到了新的颜色,就节点对应的颜色加入树状数组,考虑到不用修改,就把树状数组改成前缀和,查询就从 O ( l o g n ) O(logn) O(logn)到了 O ( 1 ) O(1) O(1)
(前缀和 s [ i ] s[i] s[i]表示, D F S DFS DFS序前 i i i的颜色为 b b b的个数)

但每一次查询的时候,都要重新算一下每个颜色为 a a a节点的子树和,又是一个 O ( n ) O(n) O(n),所以在每个颜色中预处理,查询就 O ( 1 ) O(1) O(1)
至于预处理,就按 D F S DFS DFS序,从 1 1 1 ~ n n n循环一遍,查询子树和,把答案算入该节点颜色作为 a a a的贡献里去。

最后按询问的位置排序,输出即可。

#include<bits/stdc++.h>
#include<queue>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=1e5+5;
int n,r,q,col[N];
priority_queue< pair<int,int> >p;
struct edge{
	int v,nxt;
}e[N];
int first[N],cnt=0;
inline void add(int u,int v){
	e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;
}
struct ques{
	int a,b,id,ans;
}a[N<<1];
inline bool SORT(const ques &x,const ques &y){
	return x.b>y.b;
}
inline bool Sort(const ques &x,const ques &y){
	return x.id<y.id;
}
int pos[N],end[N],tot=0;
void dfs(int x){
	pos[x]=++tot;
	for(int i=first[x];i;i=e[i].nxt)
		dfs(e[i].v);
	end[x]=tot;
}
int preans[N],s[N];
int main(){
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	n=read();r=read();q=read();
	col[1]=read();
	p.push(make_pair(col[1],1));
	for(int i=2;i<=n;i++){
		int f;
		f=read();col[i]=read();
		add(f,i);
		p.push(make_pair(col[i],i));
	}
	for(int i=1;i<=q;i++){
		a[i].a=read();a[i].b=read();
		a[i].id=i;a[i].ans=0;
	}
	sort(a+1,a+q+1,SORT);
	dfs(1);
	int tq=1,last=0;
	while(tq<=q){
		if(a[tq].b!=last){
			if(p.empty())break;
			memset(s,0,sizeof(s));
			memset(preans,0,sizeof(preans));
			while(!p.empty()){
				int x=p.top().second;
				if(col[x]!=a[tq].b)break;
				p.pop();
				s[pos[x]]++;
			}
			last=a[tq].b;
			for(int i=1;i<=n;i++)s[i]+=s[i-1];
			for(int i=1;i<=n;i++)
				preans[col[i]]+=s[end[i]]-s[pos[i]];
		}
		a[tq].ans=preans[a[tq].a];
		tq++;
	}
	sort(a+1,a+q+1,Sort);
	for(int i=1;i<=q;i++)printf("%d\n",a[i].ans);
	return 0;
}

复杂度貌似是 O ( n r ) O(nr) O(nr)的,但不知道就过了。
好像是因为 r r r太小了。
所以貌似是个错误算法。

%%%ZXY%%%

只有当颜色的个数大于根号 n n n,才处理,剩下一次 D F S DFS DFS处理完。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值