题目大意:
题目链接:https://jzoj.net/senior/#main/show/3360
给定一棵个节点的树,每个节点有一个颜色。查询两个节点之间路径上有多少种不同
的颜色,一次查询可以将一种颜色视为另一种。
思路:
考试时就写了一条链的莫队部分分+暴力的。然后写炸了。暴力和莫队各有一个点没拿到。
正解是树上莫队。就是莫队的一个升级版本。
先求出这棵树的欧拉序,在欧拉序上跑莫队,注意如果这个区间涵盖了一个点偶数次,那么这个点就是不在树上这两个点的路径之间的。因为这个点肯定在这个区间内进入了一次并且出去了一次。
所以我们的莫队不能像普通的莫队一样直接写,要先判断这个点的出现次数的奇偶,然后再选择来修改。虽然好像没有什么区别
细节比较多,调了好久,注意细节即可。
代码:
#include <map>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=200010,LG=18;
int a[N],head[N],cnt[N],ans[N],f[N][LG+1],dep[N],dfn[N],s[N],t[N],pos[N],p[N];
int n,m,tot,T,sum,num;
bool find[N];
struct Ask
{
int l,r,a,b,id;
}ask[N];
struct edge
{
int to,next;
}e[N];
int read()
{
int d=0;
char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch))
d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
bool cmp(Ask x,Ask y)
{
if (pos[x.l]<pos[y.l]) return 1;
if (pos[x.l]>pos[y.l]) return 0;
return x.r<y.r;
}
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
void dfs1(int x,int fa)
{
dfn[++num]=x;
s[x]=num;
for (int i=head[x];~i;i=e[i].next)
if (e[i].to!=fa) dfs1(e[i].to,x);
dfn[++num]=x;
t[x]=num;
}
void del(int x)
{
cnt[a[x]]--;
if (!cnt[a[x]]) sum--;
}
void add(int x)
{
if (!cnt[a[x]]) sum++;
cnt[a[x]]++;
}
void maintain(int x)
{
p[x]^=1;
if (!p[x]) del(x);
else add(x);
}
void dfs2(int x,int fa)
{
dep[x]=dep[fa]+1;
f[x][0]=fa;
for (int i=1;i<=LG;i++)
f[x][i]=f[f[x][i-1]][i-1];
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa) dfs2(v,x);
}
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for (int i=LG;i>=0;i--)
if (dep[f[x][i]]>=dep[y]) x=f[x][i];
if (x==y) return x;
for (int i=LG;i>=0;i--)
if (f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
int main()
{
freopen("data.txt","r",stdin);
freopen("ans.out","w",stdout);
memset(head,-1,sizeof(head));
n=read(); m=read();
T=sqrt(n*2);
for (int i=1;i<=n*2;i++)
pos[i]=(i-1)/T+1;
for (int i=1;i<=n;i++)
a[i]=read();
for (int i=1,x,y;i<=n;i++)
{
x=read(); y=read();
add(x,y); add(y,x);
}
dfs1(0,0);
dfs2(0,0);
for (int i=1;i<=m;i++)
{
ask[i].l=read(); ask[i].r=read();
ask[i].a=read(); ask[i].b=read();
ask[i].id=i;
int LCA=lca(ask[i].l,ask[i].r);
if (ask[i].l==LCA)
{
swap(ask[i].l,ask[i].r);
ask[i].l=t[ask[i].l];
ask[i].r=t[ask[i].r];
}
else if (ask[i].r==LCA)
{
ask[i].l=t[ask[i].l];
ask[i].r=t[ask[i].r];
}
else
{
if (t[ask[i].l]>t[ask[i].r]) swap(ask[i].l,ask[i].r);
ask[i].l=t[ask[i].l];
ask[i].r=s[ask[i].r];
}
}
sort(ask+1,ask+1+m,cmp);
int l=1,r=0;
for (int i=1;i<=m;i++)
{
for (;l<ask[i].l;l++) maintain(dfn[l]);
for (;l>ask[i].l;l--) maintain(dfn[l-1]);
for (;r<ask[i].r;r++) maintain(dfn[r+1]);
for (;r>ask[i].r;r--) maintain(dfn[r]);
int x=dfn[l],y=dfn[r];
int LCA=lca(x,y);
if (LCA!=x && LCA!=y) add(LCA);
if (cnt[ask[i].b] && cnt[ask[i].a] && ask[i].a!=ask[i].b) ans[ask[i].id]=sum-1;
else ans[ask[i].id]=sum;
if (LCA!=x && LCA!=y) del(LCA);
}
for (int i=1;i<=m;i++)
printf("%d
",ans[i]);
return 0;
}