组合树
题目大意:
有一棵树,每个点都有自己的原颜色和目标颜色(黑或白),现在深度不小于k的点可以让自己祖宗k代k个点的颜色全部取反,现在问当前树是否能变成目标树
输入样例
2
3 2
1 2
2 3
0 0 0
1 0 1
3 2
1 2
2 3
0 0 0
1 1 1
输出样例
Yes
No
样例解释
在第一个例子中,第一次选择2号点操作,1,2号点被翻转;第二次选择3
号点操作,2,3号点被翻转。即达成目标状态。
可以证明,无法将初始状态经过操作变为目标状态。
数据范围
对于前 10% 的数据,n≤5;
对于前 30% 的数据,n≤20;
对于前 50% 的数据,n≤2000;
对于前 70% 的数据,n≤50000;
对于全部数据,T≤10, k≤n≤2×105,保证数据给出的是一棵树。
解题思路:
用dfs确定点之间的关系顺便算出第k代祖先,然后根据拓扑序判断是否需要改变,如果要就用差分记录,在第k代祖先的地方打上一个符号,然后在下一个点也打上一个相反的符号,然后判断是否能行即可
代码:
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,t,k,x,y,h,tot,f[1000500],fv[1000500],sv[1000500],dt[1000500],nos[1000500],dad[1000500],dadk[1000500],head[1000500];
struct rec
{int to,next;
}a[1000500];
void dfs(int now,int dep)
{if (dep>=k) dadk[now]=dt[dep-k+1];//第k代祖先,dt表示当前在某一行的是哪个数for (int i=head[now];i;i=a[i].next)//遍历if (!dad[a[i].to])//消掉bug{dad[a[i].to]=now;//记录dt[dep+1]=a[i].to;nos[now]++;//儿子数dfs(a[i].to,dep+1);}
}
void js()
{tot=0;memset(f,0,sizeof(f));memset(nos,0,sizeof(nos));memset(dad,0,sizeof(dad));memset(dadk,0,sizeof(dadk));memset(head,0,sizeof(head));scanf("%d %d",&n,&k);for (int i=1;i<n;++i){scanf("%d %d",&x,&y);a[++tot].to=y;a[tot].next=head[x];head[x]=tot;a[++tot].to=x;a[tot].next=head[y];head[y]=tot;}for (int i=1;i<=n;++i) scanf("%d",&fv[i]);for (int i=1;i<=n;++i) scanf("%d",&sv[i]);dt[1]=1;dad[1]=-1;dfs(1,1);queue<int>d;for (int i=1;i<=n;++i)if (!nos[i])d.push(i);while(!d.empty())//拓扑排序{h=d.front();d.pop();if ((fv[h]+f[h])%2!=sv[h])//不符合的if (!dadk[h])//不能加{printf("No\n");//就不行了return;}else f[dad[h]]++,f[dad[dadk[h]]]--;//可以就打上差分符号f[dad[h]]+=f[h];//继承上去nos[dad[h]]--;if (!nos[dad[h]]) d.push(dad[h]);}printf("Yes\n");
}
int main()
{scanf("%d",&t);while (t--) js();
}