BZOJ 3757 苹果树

Description

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

有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为u的苹果出发,由树枝走到编号为v的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果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

HINT

0<=x,y,a,b<=N

N<=50000

1<=U,V,Coli<=N

M<=100000

此题存在版权,故不再支持提交,保留在此只供大家参考题面! 望见谅!

Source

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

树上莫队~

和上一道差不多,就是要求一下root的位置,不能直接用1来代替。

BZOJ上不能测,要了数据测的~

fa[i][j]数组又开小了……调到吐血tat


#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;

int n,m,a[50005],fi[100005],w[100005],ne[100005],cnt,stk[100005],top,totcas,fa[100005][18],dep[100005];
int pos[100005],num[100005],ans[100005],now,kkz,dfn[100005],jin,root;
bool b[100005];

struct node{
	int x,y,a,b,id;
}que[100001];

int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0' && ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}

void add(int u,int v)
{
	w[++cnt]=v;ne[cnt]=fi[u];fi[u]=cnt;
	w[++cnt]=u;ne[cnt]=fi[v];fi[v]=cnt;
}

void dfs(int u)
{
	dfn[u]=++kkz;
	for(int i=1;(1<<i)<=dep[u];i++) fa[u][i]=fa[fa[u][i-1]][i-1];
	int now=top;
	for(int i=fi[u];i;i=ne[i])
	  if(w[i]!=fa[u][0])
	  {
	  	fa[w[i]][0]=u;dep[w[i]]=dep[u]+1;
	  	dfs(w[i]);
	  	if(top-now>=jin)
	  	{
	  		totcas++;
	  		while(top-now>=jin) pos[stk[top--]]=totcas;
	  	}
	  }
	stk[++top]=u;
}

int lca(int u,int v)
{
	if(dep[u]<dep[v]) swap(u,v);
	int now=dep[u]-dep[v];
	for(int i=0;(1<<i)<=now;i++) if((1<<i)&now) u=fa[u][i];
	if(u==v) return u;
	for(int i=17;~i;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}

bool operator < (node u,node v)
{
	return pos[u.x]==pos[v.x] ? dfn[u.y]<dfn[v.y]:pos[u.x]<pos[v.x];
}

void xo(int u)
{
	if(b[u]) now-=((--num[a[u]])==0);
	else now+=((++num[a[u]])==1);
	b[u]^=1;
}

void chan(int u,int v)
{
	while(u!=v)
	  if(dep[u]>dep[v]) xo(u),u=fa[u][0];
	  else xo(v),v=fa[v][0];
}

int main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++)
	{
		int x=read(),y=read();
		if(!(x*y)) root=x+y;
		else add(x,y);
	}
	jin=sqrt(n);dfs(root);
	while(top) pos[stk[top--]]=totcas;
	for(int i=1;i<=m;i++)
	{
		que[i].x=read();que[i].y=read();que[i].a=read();que[i].b=read();que[i].id=i;
		if(pos[que[i].x]>pos[que[i].y]) swap(que[i].x,que[i].y);
	}
	sort(que+1,que+m+1);
	int x=root,y=root,k;
	for(int i=1;i<=m;i++)
	{
		if(x!=que[i].x) chan(x,que[i].x),x=que[i].x;
		if(y!=que[i].y) chan(y,que[i].y),y=que[i].y;
		k=lca(x,y);xo(k);
		if(que[i].a==que[i].b || !(num[que[i].a]*num[que[i].b])) ans[que[i].id]=now;
		else ans[que[i].id]=now-1;xo(k);
	}
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值