3757: 苹果树
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 360 Solved: 111
[ Submit][ Status]
Description
神犇家门口种了一棵苹果树。苹果树作为一棵树,当然是呈树状结构,每根树枝连接两个苹果,每个苹果都可以沿着一条由树枝构成的路径连到树根,而且这样的路径只存在一条。由于这棵苹果树是神犇种的,所以苹果都发生了变异,变成了各种各样的颜色。我们用一个到n之间的正整数来表示一种颜色。树上一共有n个苹果。每个苹果都被编了号码,号码为一个1到n之间的正整数。我们用0代表树根。只会有一个苹果直接根。
有许许多多的人来神犇家里膜拜神犇。可神犇可不是随便就能膜拜的。前来膜拜神犇的人需要正确回答一个问题,才能进屋膜拜神犇。这个问题就是,从树上编号为u的苹果出发,由树枝走到编号为v的苹果,路径上经过的苹果一共有多少种不同的颜色(包括苹果u和苹果v的颜色)?不过神犇注意到,有些来膜拜的人患有色盲症。具体地说,一个人可能会认为颜色a就是颜色b,那么他们在数苹果的颜色时,如果既出现了颜色a的苹果,又出现了颜色b的苹果,这个人只会算入颜色b,而不会把颜色a算进来。
神犇是一个好人,他不会强人所难,也就会接受由于色盲症导致的答案错误(当然答案在色盲环境下也必须是正确的)。不过这样神犇也就要更改他原先数颜色的程序了。虽然这对于神犇来说是小菜一碟,但是他想考验一下你。你能替神犇完成这项任务吗?
Input
Output
输出一共m行,每行仅包含一个整数,代表这个人应该数出的颜色种数。
Sample Input
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
1
2
HINT
树上莫队。
对于序列的莫队是先把序列分块,再按照左端点处在的块数作为第一关键字,右端点作为第二关键字对询问排序,再进行转移就可以了。
对于树的同理。做了【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("%dn",Ans[i]);
return 0;
}

感悟:
1.tle是因为lca求错了。。
最后
以上就是笨笨酸奶最近收集整理的关于【BZOJ 3757】 苹果树的全部内容,更多相关【BZOJ内容请搜索靠谱客的其他文章。
发表评论 取消回复