题目描述
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]);
}