神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个1到n之间的正整数来表示一种颜色。树上一共有n个苹果。每个苹果都被编了号码,号码为一个1到n之间的正整数。我们用0代表树根。只会有一个苹果直接与根相连。
有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为u的苹果出发,由树枝走到编号为v的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果u和苹果v的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色a就是颜色b,那么他们在数苹果的颜色时,如果既出现了颜色a的苹果,又出现了颜色b的苹果,这个人只会算入颜色b,而不会把颜色a算进来。
神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?
输出一共m行,每行仅包含一个整数,代表这个人应该数出的颜色种数。
题解:树上莫队。
首先要对树上的点进行分块,分块方式与上一题bzoj 1086 相同。
然后对于每个询问以左端点所在的块为第一关键字,右端点的dfs序为第二关键字排序。
那么如何进行区间的转移呢?
网上有个神奇的证明:
知道这个T(targetV, curU)= T(curV, curU) xor T(curV, targetV)结论就可以每次进行转移了。
#include
#include
#include
#include
#include
#define N 50003
using namespace std;
int n,m,sum,top;
int c[N],dfsn[N],v[N*2],next[N*2],point[N],deep[N],root,sz,block;
int fa[N][20],mi[20],vis[N],num[N],ans[N],cnt,tot,belong[N],st[N];
struct data{
int a,b,u,v,id;
}q[100003];
int cmp(data a,data b)
{
if (belong[a.u]==belong[b.u]) return dfsn[a.v]=block)//分块
{
cnt++;
for (int j=1;j<=size;j++)
belong[st[top--]]=cnt;
size=0;
}
}
st[++top]=x;
return size+1;
}
int lca(int x,int y)//求最近公共祖先
{
if (deep[x]>i&1) x=fa[x][i];
if (x==y) return x;
for (int i=16;i>=0;i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void reserve(int x)//将点的存在性取反
{
if (!vis[x])
{
vis[x]=1; num[c[x]]++;
if (num[c[x]]==1) sum++;
}
else
{
vis[x]=0; num[c[x]]--;
if (num[c[x]]==0) sum--;
}
}
void solve(int x,int y)//更改x,y路径上除lca以外的点的存在性
{
while (x!=y)
{
if (deep[x]>deep[y])
{
reserve(x); x=fa[x][0];
}
else
{
reserve(y); y=fa[y][0];
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%d",&c[i]);
for (int i=1;i<=n;i++)
{
int x,y; scanf("%d%d",&x,&y);
if (!x) root=y;//因为0是没有苹果的,所有我们要寻找有苹果的根
else if (!y) root=x;
else add(x,y);
}
mi[0]=1; block=sqrt(n);
for (int i=1;i<=16;i++) mi[i]=mi[i-1]*2;
deep[root]=1;
dfs(root,0);
++cnt;
while (top)
belong[st[top--]]=cnt;
for (int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&q[i].u,&q[i].v,&q[i].a,&q[i].b);
if (dfsn[q[i].u]>dfsn[q[i].v]) swap(q[i].u,q[i].v);
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
int t=lca(q[1].u,q[1].v);
solve(q[1].u,q[1].v);
reserve(t);
ans[q[1].id]=sum;
if (num[q[1].a]&&num[q[1].b]&&q[1].a!=q[1].b) ans[q[1].id]--;//色盲特判
reserve(t);
for (int i=2;i<=m;i++)
{
solve(q[i-1].u,q[i].u); solve(q[i-1].v,q[i].v);
int t=lca(q[i].u,q[i].v);
reserve(t);
ans[q[i].id]=sum;
if (num[q[i].a]&&num[q[i].b]&&q[i].a!=q[i].b) ans[q[i].id]--;
reserve(t);
}
for (int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}