神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个到n之间的正整数来表示一种颜色。树上一共有n个苹果。每个苹果都被编了号码,号码为一个1到n之间的正整数。我们用0代表树根。只会有一个苹果直接根。
有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为u的苹果出发,由树枝走到编号为v的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果u和苹果v的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色a就是颜色b,那么他们在数苹果的颜色时,如果既出现了颜色a的苹果,又出现了颜色b的苹果,这个人只会算入颜色b,而不会把颜色a算进来。
神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?
输出一共m行,每行仅包含一个整数,代表这个人应该数出的颜色种数。
树上莫队。
对于序列的莫队是先把序列分块,再按照左端点处在的块数作为第一关键字,右端点作为第二关键字对询问排序,再进行转移就可以了。
对于树的同理。做了【bzoj1086】树分块就明白了,按照同样的方法对询问排序,那么关键就在于如何转移了:
vfk的做法:
行操作),再对(curU,targetU)上的点取反(对lca不进行操作),就转移过来了,然后单独对lca取反,把答案保存后,
再把lca取反,因此仍是相当于对lca没有操作。
对于色盲的问题,只要在算出答案之后判断一下就可以。
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #define N 50005 using namespace std; int dfn[N],Ans[N*3],n,m,top=0,c[N],h[N],tot=0,cnt=0,be[N],dep[N],b[20]; int a,dfs_clock=0,f[N][17],k,ans=0,s[N],v[N],num[N]; int read() { int tmp=0,fu=1; char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') fu=-1; for (;ch>='0'&&ch<='9';ch=getchar()) tmp=tmp*10+ch-'0'; return tmp; } struct Query { int x,y,a,b,id; }q[N*3]; struct edge { int y,ne; }e[N*3]; void Addedge(int x,int y) { e[++tot].y=y; e[tot].ne=h[x]; h[x]=tot; } void dfs(int x) { dfn[x]=++dfs_clock; int bottom=top; for (int i=h[x];i;i=e[i].ne) if (e[i].y!=f[x][0]) { int y=e[i].y; dep[y]=dep[x]+1; f[y][0]=x; dfs(y); if (top-bottom>=k) { cnt++; while (top!=bottom) be[s[top--]]=cnt; } } s[++top]=x; } bool cmp(Query a,Query b) { if (be[a.x]==be[b.x]) return dfn[a.y]<dfn[b.y]; return be[a.x]<be[b.x]; } void Prepare() { b[0]=1; for (int i=1;i<=16;i++) b[i]=b[i-1]<<1; for (int i=1;i<=16;i++) for (int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1]; } void Move(int &x,int deep) { for (int i=16;i>=0;i--) if (dep[f[x][i]]>=deep) x=f[x][i]; } int Getlca(int x,int y) { if (dep[x]>dep[y]) swap(x,y); Move(y,dep[x]); if (x==y) return x; for (int i=16;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } void Calc(int x) { if (!v[x]) { v[x]=1; num[c[x]]++; if (num[c[x]]==1) ans++; } else { v[x]=0; num[c[x]]--; if (!num[c[x]]) ans--; } } void Solve(int x,int y) { while (x!=y) { if (dep[x]<dep[y]) swap(x,y); Calc(x); x=f[x][0]; } } int main() { n=read(),m=read(); for (int i=1;i<=n;i++) c[i]=read(); int root; for (int i=1;i<=n;i++) { int x,y; x=read(),y=read(); if (x==0) root=y; else if (y==0) root=x; else Addedge(x,y),Addedge(y,x); } k=sqrt(n); dep[root]=1; dfs(root); cnt++; while (top) be[s[top--]]=cnt; for (int i=1;i<=m;i++) { q[i].x=read(),q[i].y=read(),q[i].a=read(),q[i].b=read(); if (dfn[q[i].x]>dfn[q[i].y]) swap(q[i].x,q[i].y); q[i].id=i; } sort(q+1,q+1+m,cmp); Prepare(); q[0].x=q[0].y=1; for (int i=1;i<=m;i++) { a=Getlca(q[i].x,q[i].y); Solve(q[i-1].x,q[i].x); Solve(q[i-1].y,q[i].y); Calc(a); Ans[q[i].id]=ans; if (q[i].a!=q[i].b&&num[q[i].a]&&num[q[i].b]) Ans[q[i].id]--; Calc(a); } for (int i=1;i<=m;i++) printf("%d\n",Ans[i]); return 0; }
感悟:
1.tle是因为lca求错了。。