jzoj3360. 苹果树(树上莫队)

题目描述

Description

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

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

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

Input

输入第一行为两个整数N和M,分别代表树上苹果的个数和前来膜拜的人数。

接下来的一行包含N个数,第i个数代表编号为i的苹果的颜色coli。

接下来有N行,每行包含两个数x和y,代表有一根树枝连接了苹果x和y(或者根和一个苹果)。

接下来有M行,每行包含四个整数u、v、a和b,代表这个人要数苹果u到苹果v的颜色种数,同时这个人认为颜色a就是颜色b。如果a=b=0,则代表这个人没有患色盲症。

Output
输出一共M行,每行仅包含一个整数,代表这个人应该数出的颜色种数。

Sample Input
5 3

1 1 3 3 2

0 1

1 2

1 3

2 4

3 5

1 4 0 0

1 4 1 3

1 4 1 2

Sample Output
2

1

2

Data Constraint
在这里插入图片描述

题解

一眼树上莫队,然后发现并不会写。。。
没学之前感觉很神仙


由于n≤50000,所以很显然就是n√n级别的算法
一条链的情况可以用普通莫队,现在要拓展到树上

欧拉序

比如这样一棵树:
在这里插入图片描述
欧拉序类似(?)括号序,在dfs时每个点进栈时记录一次,出栈时记录一次
所以上图的欧拉序为
1244566775238831

性质

欧拉序的用处就是把树上的一段路径转为序列中的一段
对于一段x–y(x的dfs序在y前)之间的路径:

①x和y之间没有祖孙关系(lca≠x/y)
那么x–y路径上的点为序列中x第二次出现位置到y第一次出现位置中出现一次的点,再加上x和y的lca
比如6–8,那么在序列中对应为
1244566775238831
去掉出现了两次的7并加上lca1,剩下的就是6–5--2–1–3--8

②x是y的祖先(因为x的dfs序在y前,所以y不可能是x的祖先)
那么x–y路径上的点为序列中x第一次出现位置到y第一次出现位置中出现一次的点
比如1–7,那么在序列中对应为
1244566775238831
去掉出现了两次的4、6,剩下的就是1–2--5–7

证明什么的感性理解画几个图,可以发现非路径上的点要么没走到,要么先进栈再出栈

树上莫队

这样把树上的询问转为了序列上的,然后做普通莫队就行了

分块写法

由于莫队的时间是O(玄),所以还有另一种稳定O(n√n)的分块写法:
(其实就是回滚莫队
对于每个询问的l分块,如果l和r在同一块就先暴力处理
然后对于l在同一块的按r排序,那么(l所在块+1)往后的部分就可以线性维护
因为剩余部分大小不超过√n,所以每次询问可以暴力添加/删除

这样的时间稳定O(n√n),而且可以处理一些只能插入不能删除的问题(比如最大值)
因为可以在线性维护的基础上再开一个数组来维护当前询问的答案,询问完后清空相关位置即可

缺点就是码量比较清真

code

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
using namespace std;

int a[100001][2];
int v[50001];
int ls[50001];
int bg[50001];
int ed[50001];
int c[50001];
int d[100001];
int D[50001];
bool bz[50001];
int fa[50001][16];
int Ans[100001];
struct type{
	int x,y,A,B,s,lca,id;
} b[100001];
int N,tot,n,m,i,j,k,l,len,root,x,y,ans,S;
bool Bz;

void swap(int &x,int &y)
{
	int z=x;
	x=y;
	y=z;
}

bool cmp(type x,type y)
{
	return x.s<y.s || x.s==y.s && x.y<y.y;
}

void New(int x,int y)
{
	++len;
	a[len][0]=y;
	a[len][1]=ls[x];
	ls[x]=len;
}

void dfs(int Fa,int t)
{
	int i;
	
	fa[t][0]=Fa;
	fo(i,1,15)
	fa[t][i]=fa[fa[t][i-1]][i-1];
	
	D[t]=D[Fa]+1;
	
	bg[t]=++N;
	d[N]=t;
	
	for (i=ls[t]; i; i=a[i][1])
	if (a[i][0]!=Fa)
	dfs(t,a[i][0]);
	
	ed[t]=++N;
	d[N]=t;
}

int lca(int x,int y)
{
	int i;
	
	if (D[x]<D[y])
	swap(x,y);
	
	fd(i,15,0)
	if (D[fa[x][i]]>=D[y])
	x=fa[x][i];
	
	fd(i,15,0)
	if (fa[x][i]!=fa[y][i])
	{
		x=fa[x][i];
		y=fa[y][i];
	}
	
	if (x!=y)
	x=fa[x][0];
	return x;
}

int main()
{
//	freopen("apple1.in","r",stdin);
//	freopen("apple2.in","r",stdin);
//	freopen("A7_12_1.in","r",stdin);
	
	scanf("%d%d",&n,&m);
	fo(i,1,n)
	scanf("%d",&v[i]);
	fo(i,1,n)
	{
		scanf("%d%d",&j,&k);
		
		if (!j)
		root=k;
		else
		if (!k)
		root=j;
		else
		{
			New(j,k);
			New(k,j);
		}
	}
	
	N=0;
	dfs(0,root);
	S=floor(sqrt(N));
	
//	fo(i,1,N)
//	cout<<d[i]<<" ";cout<<endl;
	
	tot=0;
	fo(i,1,m)
	{
		scanf("%d%d%d%d",&b[i].x,&b[i].y,&b[i].A,&b[i].B);
		b[i].id=i;
		b[i].lca=lca(b[i].x,b[i].y);
		
		if (bg[b[i].x]>bg[b[i].y])
		swap(b[i].x,b[i].y);
		
		if (b[i].lca==b[i].x)
		{
			b[i].x=bg[b[i].x];
			b[i].y=bg[b[i].y];
			b[i].lca=-1;
		}
		else
		{
			b[i].x=ed[b[i].x];
			b[i].y=bg[b[i].y];
		}
		
		b[i].s=b[i].x/S+1;
		
		if (b[i].s==b[i].y/S+1)
		{
			ans=0;
			fo(j,b[i].x,b[i].y)
			{
				bz[d[j]]^=1;
				if (bz[d[j]])
				{
					++c[v[d[j]]];
					if (c[v[d[j]]]==1)
					++ans;
				}
				else
				{
					--c[v[d[j]]];
					if (!c[v[d[j]]])
					--ans;
				}
			}
			if (b[i].lca>-1)
			{
				bz[b[i].lca]^=1;
				if (bz[b[i].lca])
				{
					++c[v[b[i].lca]];
					if (c[v[b[i].lca]]==1)
					++ans;
				}
				else
				{
					--c[v[b[i].lca]];
					if (!c[v[b[i].lca]])
					--ans;
				}
			}
			
			if (b[i].A!=b[i].B && c[b[i].A] && c[b[i].B])
			Ans[b[i].id]=ans-1;
			else
			Ans[b[i].id]=ans;
			
			fo(j,b[i].x,b[i].y)
			{
				bz[d[j]]^=1;
				if (bz[d[j]])
				{
					++c[v[d[j]]];
					if (c[v[d[j]]]==1)
					++ans;
				}
				else
				{
					--c[v[d[j]]];
					if (!c[v[d[j]]])
					--ans;
				}
			}
			if (b[i].lca>-1)
			{
				bz[b[i].lca]^=1;
				if (bz[b[i].lca])
				{
					++c[v[b[i].lca]];
					if (c[v[b[i].lca]]==1)
					++ans;
				}
				else
				{
					--c[v[b[i].lca]];
					if (!c[v[b[i].lca]])
					--ans;
				}
			}
		}
		else
		b[++tot]=b[i];
	}
	
//	fo(i,1,m)
//	cout<<b[i].x<<" "<<b[i].y<<" "<<b[i].lca<<endl;
//	return 0;
	
	if (tot)
	sort(b+1,b+tot+1,cmp);
	
	fo(i,1,tot)
	{
		if (b[i-1].s!=b[i].s)
		{
			memset(c,0,sizeof(c));
			memset(bz,0,sizeof(bz));
			ans=0;
			
			fo(j,b[i].s*S,b[i].y)
			{
				bz[d[j]]^=1;
				if (bz[d[j]])
				{
					++c[v[d[j]]];
					if (c[v[d[j]]]==1)
					++ans;
				}
				else
				{
					--c[v[d[j]]];
					if (!c[v[d[j]]])
					--ans;
				}
			}
		}
		else
		{
			fo(j,b[i-1].y+1,b[i].y)
			{
				bz[d[j]]^=1;
				if (bz[d[j]])
				{
					++c[v[d[j]]];
					if (c[v[d[j]]]==1)
					++ans;
				}
				else
				{
					--c[v[d[j]]];
					if (!c[v[d[j]]])
					--ans;
				}
			}
		}
		fd(j,b[i].s*S-1,b[i].x)
		{
			bz[d[j]]^=1;
			if (bz[d[j]])
			{
				++c[v[d[j]]];
				if (c[v[d[j]]]==1)
				++ans;
			}
			else
			{
				--c[v[d[j]]];
				if (!c[v[d[j]]])
				--ans;
			}
		}
		
//		if (b[i-1].s!=b[i].s)
//		cout<<endl;
		
		if (b[i].lca>-1)
		{
			bz[b[i].lca]^=1;
			if (bz[b[i].lca])
			{
				++c[v[b[i].lca]];
				if (c[v[b[i].lca]]==1)
				++ans;
			}
			else
			{
				--c[v[b[i].lca]];
				if (!c[v[b[i].lca]])
				--ans;
			}
		}
		
		if (b[i].A!=b[i].B && c[b[i].A] && c[b[i].B])
		Ans[b[i].id]=ans-1;
		else
		Ans[b[i].id]=ans;
		
		fd(j,b[i].s*S-1,b[i].x)
		{
			bz[d[j]]^=1;
			if (bz[d[j]])
			{
				++c[v[d[j]]];
				if (c[v[d[j]]]==1)
				++ans;
			}
			else
			{
				--c[v[d[j]]];
				if (!c[v[d[j]]])
				--ans;
			}
		}
		if (b[i].lca>-1)
		{
			bz[b[i].lca]^=1;
			if (bz[b[i].lca])
			{
				++c[v[b[i].lca]];
				if (c[v[b[i].lca]]==1)
				++ans;
			}
			else
			{
				--c[v[b[i].lca]];
				if (!c[v[b[i].lca]])
				--ans;
			}
		}
	}
	
	fo(i,1,m)
	printf("%d\n",Ans[i]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值