3360. 【NOI2013模拟】苹果树

题目描述

  • 神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个1到N之间的正整数来表示一种颜色。树上一共有N个苹果。每个苹果都被编了号码,号码为一个1到N之间的正整数。我们用0代表树根。只会有一个苹果直接连到树根。

  • 有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为N的苹果出发,由树枝走到编号为N的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果u和苹果v的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色a就是颜色b,那么他们在数苹果的颜色时,如果既出现了颜色a的苹果,又出现了颜色b的苹果,这个人只会算入颜色b,而不会把颜色a算进来。

  • 神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?

数据范围

n < = 5 1 0 4 , m < = 1 0 5 n<=5*10^4,m<=10^5

题目分析

  • 听说又是一道鬼畜的模板题。
  • 这道题显然 使用树上莫队。
  • 普通莫队是在序列上做的,如何转移到树上呢?
  • 最直接的思路:把树强制转成序列。
  • 恭喜你,你猜对了。

树转序列

  • 采用一种神奇的东西(具体叫什么序也不清楚)。
  • 就是在遍历一棵树的时候,如果递归到了这个点,把它扔进序列中,如果回溯,再一次把它扔进序列中。
  • 盗个图:
  • 在这里插入图片描述
  • 那么这个序列就是 A B E E F K K F B C G G H H I I C A ABEEFKKFBCGGHHIICA
  • 我们发现,对于一条路径 x > y x->y ,我们分类讨论。
  • 先设 s t [ i ] st[i] i i 这个点第一次在序列中的位置, e d [ i ] ed[i] 表示的是第二次,也是最后一次。
  • x x y y 中一个为它们的 L C A LCA ,那么它们的路径就是序列 [ s t [ x ] , s t [ y ] ] [st[x],st[y]] 只出现过一次的点。
  • 例如 A > K A->K ,序列为 A B E E F K ABEEFK
  • 其中出现过一次的为 A , B , F , K A,B,F,K
  • 而若 x x y y 均不为 L C A LCA ,那么它们的路径就是序列 [ e d [ x ] , s t [ y ] ] [ed[x],st[y]] 只出现过一次的点。
  • 例如 F > H F->H ,序列为 F B C G G H FBCGGH
  • 其中出现过一次的为 F , B , C , H F,B,C,H
  • 我们发现这里面没有包含它们的 L C A LCA —— A A 点,这要特殊处理。
  • 然后就把树上路径转化为了序列上的一段区间,用普通莫队的方法处理即可。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=5e4+100,M=1e5+100,maxn=16;
struct node{
	int x,y,next;
}a[2*N];int len,last[N];
struct ques{
	int x,y,id,a,b,lca;
}tr[M];
bool bk[N];int st[N],ed[N];
int sum=0;int fa[N][20],Be[2*N];
bool cmp(ques n1,ques n2){
	if(Be[n1.x]!=Be[n2.x]) return (Be[n1.x]<Be[n2.x]);
	else return (n1.y<n2.y);
}
int coun[N],count2[N],g[2*N],deep[N];
void ins(int x,int y){
	a[++len].x=x;a[len].y=y;
	a[len].next=last[x];last[x]=len;
}
void dfs(int x){
	bk[x]=false;st[x]=++sum;g[sum]=x;
	for(int i=1;i<=maxn;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int k=last[x];k;k=a[k].next){
		int y=a[k].y;
		if(bk[y]) fa[y][0]=x,deep[y]=deep[x]+1,dfs(y);
	}
	ed[x]=++sum;g[sum]=x;
}
int co[N];
int findlca(int x,int y){
	for(int i=maxn;i>=0;i--){
		if(deep[fa[x][i]]>=deep[y]) x=fa[x][i];
		if(deep[fa[y][i]]>=deep[x]) y=fa[y][i];
	}
	for(int i=maxn;i>=0;i--){
		if(fa[x][i]!=fa[y][i])
			x=fa[x][i],y=fa[y][i];
	}
	if(x!=y) x=fa[x][0],y=fa[y][0];
	return x;
}
int Ans[M];
int main()
{
	int n,m;scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&co[i]);
	len=0;memset(last,0,sizeof(last));
	for(int i=1;i<=n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		ins(x,y);ins(y,x);
	}
	memset(bk,true,sizeof(bk));
	dfs(0);
	int T=sqrt(2*n);
	for(int i=1;i<=2*n;i++) Be[i]=(i-1)/T+1;
	for(int i=1;i<=m;i++){
		int x,y;scanf("%d%d",&x,&y);
		scanf("%d%d",&tr[i].a,&tr[i].b);
		if(st[x]>st[y]) {int t=x;x=y;y=t;}
		int lca=findlca(x,y);tr[i].lca=lca;
		if(lca==x||y==lca) tr[i].x=st[x],tr[i].y=st[y];
		else tr[i].x=ed[x],tr[i].y=st[y];
		tr[i].id=i;
	}
	sort(tr+1,tr+m+1,cmp);
	int L=1,R=1,ans=0;coun[co[g[1]]]++;count2[g[1]]++;
	for(int i=1;i<=m;i++){
		while(L>tr[i].x){
			count2[g[L-1]]++;
			if(count2[g[L-1]]==2){
				if(coun[co[g[L-1]]]==1&&g[L-1]) ans--;
				coun[co[g[L-1]]]--;
			}
			else{
				if(coun[co[g[L-1]]]==0&&g[L-1]) ans++;
				coun[co[g[L-1]]]++;
			}
			L--;
		}
		while(R<tr[i].y){
			count2[g[R+1]]++;
			if(count2[g[R+1]]==2){
				if(coun[co[g[R+1]]]==1&&g[R+1]) ans--;
				coun[co[g[R+1]]]--;
			}
			else{
				if(coun[co[g[R+1]]]==0&&g[R+1]) ans++;
				coun[co[g[R+1]]]++;
			}
			R++;
		}
		while(L<tr[i].x){
			count2[g[L]]--;
			if(count2[g[L]]==0){
				if(coun[co[g[L]]]==1&&g[L]) ans--;
				coun[co[g[L]]]--;
			}
			else{
				if(coun[co[g[L]]]==0&&g[L]) ans++;
				coun[co[g[L]]]++;
			}
			L++;
		}
		while(R>tr[i].y){
			count2[g[R]]--;
			if(count2[g[R]]==0){
				if(coun[co[g[R]]]==1&&g[R]) ans--;
				coun[co[g[R]]]--;
			}
			else{
				if(coun[co[g[R]]]==0&&g[R]) ans++;
				coun[co[g[R]]]++;
			}
			R--;
		}
		int t=ans;int LCA=co[tr[i].lca];
		if(!coun[LCA]) ans++;coun[LCA]++;
		if(tr[i].a!=tr[i].b&&coun[tr[i].a]&&coun[tr[i].b]) ans--;
		Ans[tr[i].id]=ans;ans=t;coun[LCA]--;
	}
	for(int i=1;i<=m;i++) printf("%d\n",Ans[i]);
	return 0;
}
发布了58 篇原创文章 · 获赞 12 · 访问量 8552

猜你喜欢

转载自blog.csdn.net/fengqiyuka/article/details/95656384