AT2675
题目:Two Trees
感觉二分图的题解稍微显得比较少,由于最近本蒟蒻在学二分图,就决定来用二分图写一下这道题。
和大家的思路一样,都是对每个节点儿子数量的奇偶性分类讨论进行构造。
本蒟蒻第一眼看到这道题的时候,觉得这道题真的太难了。于是,本蒟蒻决定把它改的简单一点。请看:
-
如果只有一棵树的话,那或许简单了一点,但对于本蒟蒻来说还是太难了。
-
于是,本蒟蒻决定再将它变得简单一点,如果这棵树的深度为二的话,那这道题似乎就没有那么难了。
那就让我们从最简单的情况开始思考。
对于一个深度为二的树,我们需要做的就是把根的儿子们两两配对,每一对儿子都是一个权值为
那么问题来了,如何表示两个节点的“配对”关系呢?
你想啊,有一对节点,一个是 小题大做地把“一对”的儿子节点连起来。
解决了这个问题,那就让我们升级难度,如果只有一棵树的话,那应该如何构造一个合法解呢?
考虑每个节点,它必然会有许许多多的儿子,你可以发挥想象力,把以它的每一个儿子为根节点的子树想象成一个大大的节点,而题目中规定了,这个大大的节点总权值一定是
那不就是上面那个问题了吗。 我们也可以同样地,按照节点的儿子数量进行分类讨论,并把“一对”的儿子连起来。
等等,刚才我们可以连起来,是因为它的“儿子”是一个个节点;现在呢,它的“儿子”是一棵棵子树,而树和树是没法连起来的。
怎么办呢?
为了解决这个问题,我们引入“决定点”的概念。
由于我们连边的目的是把一对“
这不就好了吗,接下来的问题就是 如何求出一棵子树的“决定点”。
还是考虑对儿子数进行分类讨论:
-
如果是偶数个儿子的话,那么它的儿子们可以两两配对,就抵消了,所以以它为根的子树的“决定点”就是它本身。
-
如果是奇数个儿子的话,会有一个落单的儿子,由于节点本身权值是
0 ,它的所有儿子中除去落单儿子加起来也是0 ,所以以这个节点为根的子树的“决定点”就是落单儿子子树的决定点。
然后正常连边就可以了。
解决了这些,两棵树这个问题就简单了,对于两棵树都按照法则加一次边就可以了。
至于为什么形成的图会是二分图?抱歉证明不是本蒟蒻的强项,感性地解释一下就是,同一棵树内,由于一棵子树的“决定点”只会被引用一次,也就是连一次边,所以一个节点的度数一定是 我只能扯到这里了……
上代码(AC记录)
#include<cstdio>
#include<cstring>
//#define zczc
using namespace std;
const int N=100010;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
wh*=f;return;
}
int m;
bool odd[N],odd2[N];//在两棵树中某个节点的孩子数量是否为奇数
//graph中是二分图部分
namespace graph{
struct edge{
int t,next;
}e[N<<1];
int head[N],esum;
inline void add(int fr,int to){
esum++;
e[esum].t=to;
e[esum].next=head[fr];
head[fr]=esum;
return;
}
bool vis[N],black[N];
//二分图标记颜色
void dfs(int wh,bool color){
vis[wh]=true;
black[wh]=color;
for(int i=head[wh],th;i;i=e[i].next){
th=e[i].t;
if(vis[th])continue;
dfs(th,!color);
}
return;
}
}
//tree中是树的部分
//个人感觉分开来写要清晰一些
namespace tree{
struct edge{
int t,next;
}e[N];
int head[N],esum;
inline void add(int fr,int to){
esum++;
e[esum].t=to;
e[esum].next=head[fr];
head[fr]=esum;
return;
}
int decide[N];//标记每个节点为根的子树的“决定点”
bool second;
void dfs(int wh){
bool oddnum=false;//动态显示孩子数量的奇偶性
int son=0,last=0;//son是落单的孩子,last是方便“配对”孩子用的
for(int i=head[wh];i;i=e[i].next){
oddnum=!oddnum;
dfs(e[i].t);
son=e[i].t;
if(last==0)last=e[i].t;
else{
graph::add(decide[last],decide[e[i].t]);
graph::add(decide[e[i].t],decide[last]);
last=0;
}
}
if(second)odd2[wh]=oddnum;
else odd[wh]=oddnum;
//如果有奇数个孩子就取决于落单的孩子
if(oddnum)decide[wh]=decide[son];
//如果有偶数个孩子就自己决定
else decide[wh]=wh;
return;
}
void solve(){
//输入,没什么好说的
read(m);
int s1,root;
for(int i=1;i<=m;i++){
read(s1);
if(s1==-1)root=i;
else add(s1,i);
}
dfs(root);
memset(head,0,sizeof(head));
esum=0;
for(int i=1;i<=m;i++){
read(s1);
if(s1==-1)root=i;
else add(s1,i);
}
second=true;
dfs(root);
for(int i=1;i<=m;i++){
if(odd[i]!=odd2[i]){
printf("IMPOSSIBLE\n");
return;
}
}
printf("POSSIBLE\n");
for(int i=1;i<=m;i++){
if(graph::vis[i])continue;
graph::dfs(i,true);
}
for(int i=1;i<=m;i++){
if(odd[i])printf("0 ");
else{
if(graph::black[i])printf("1 ");
else printf("-1 ");
}
}
return;
}
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
tree::solve();
return 0;
}
本蒟蒻写了差不多一个小时,管理员求过,谢谢啦。
注:先前没注意