Atcoder题解:Agc018_f

首先观察这个奇怪的子树为 \(1\)\(-1\) 的限制。

看不出来性质,润了。

我们不如直接把 \(A\) 树和 \(B\) 树拆开,变成两棵树,然后在树上留一下匹配的性质。

第一,我们对着样例构造一下,发现似乎有解的样例都有 \(abs(X_i)\le 1\) 的解。

这就提示我们猜用 \(-1,0,1\) 就够了。为什么?我们先这么猜,然后构造出来不就是了。

然后,我们发现,当前点是偶数当且仅当自己在两棵树的儿子数量都是奇数,因为每一个儿子的 \(sum\) 都是 \(\pm 1\),所以每个儿子会改变一次自己的奇偶性。而最终要求是奇数,那么奇数个儿子自己是偶数,偶数个儿子自己是奇数。注意这一点在两棵树都是要满足的,所以如果两棵树上某点的儿子个数奇偶性不同,就一定无解了。

至于剩下的还会无解吗?不会了。为什么?构造出来不就是了。

现在我们来讲怎么构造。

我们把 \(\pm 1\) 映射到图上,就变成了每个点的出度和入度。我们把两棵树拆开,每棵内部分别连边,这些边要么指向父亲要么指向儿子,很明显,我们算某个子树上点的总度数的时候,除了子树的根到它父亲的边,其他的边是不会有贡献的。

然后,我们在两棵树的对应点之间连边,从 A 指向 B 或者从 B 指向 A,注意我们只去构造 \(-1,0,1\) 的解,所以偶数的就是 \(0\),我们只在儿子个数为奇数的点连边。

这里看起来就有些怪,因为这样在 A 树点上的贡献和 B 树点的贡献就不一样了。但是我们发现它正好是相反数,那么我们直接把 B 树的点权全部取反,也不会有任何影响。(因为答案要求的是 \(abs=1\)

然后我们发现,只要我们重新定向这个图,让每个子树的度数和都是 \(0\) 即可。然后除去子树根那条边的影响,就是 \(\pm 1\)

然后我们发现,这是一张半无向欧拉图。除了两个根节点以外,所有的点的度数都是偶数(因为我们给每个是奇数的都补了一条边),而根节点是奇数,所以就可以通过 \(O(n+m)\) 的 dfs 插入环算法找欧拉路径。或者我们也可以在两个根节点之间连一条边然后通过欧拉回路重定向,得到答案就好了。

感觉这个题有点厉害,其实从头到尾好多结论都是边猜边做,只是抱着一个“一定能找到答案”的信念去构造,最后居然真的构造出来了。

代码难度不高,但是我不会无向图欧拉回路,去背了个 dfs 插入环的板子,暂时还不会证明这个算法,学了个会证的 Fluery,居然还要动态图优化到 \(polylog\),晕了。

int n,fa[100005],fb[100005],ra,rb,res[100005],cnt;
int to[600005],used[300005];
vt<int>va[100005],vb[100005];
vt<pii>vv[200005];
inline void dfs(int x){
	while(vv[x].size()){
		int y=vv[x].back().first,z=vv[x].back().second;
		vv[x].pop_back();
		if(!used[z]){
			used[z]=1;
			if(to[z]){
				if(y>x)res[to[z]]=1;
				else res[to[z]]=-1;
			}
			dfs(y);
		}
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	rp(i,n){
		cin>>fa[i];
		if(fa[i]==-1)ra=i;
		else va[fa[i]].pb(i);
	}
	rp(i,n){
		cin>>fb[i];
		if(fb[i]==-1)rb=i;
		else vb[fb[i]].pb(i);
	}
	rp(i,n)if((va[i].size()&1)!=(vb[i].size()&1)){
		cout<<"IMPOSSIBLE"<<endl;
		return 0;
	}
	rp(i,n)if(!(va[i].size()&1)){
		++cnt;
		vv[i].pb({i+n,cnt});
		vv[i+n].pb({i,cnt});
		to[cnt]=i;
	}
	rp(i,n)if(fa[i]!=-1){
		++cnt;
		vv[i].pb({fa[i],cnt});
		vv[fa[i]].pb({i,cnt});
	}
	rp(i,n)if(fb[i]!=-1){
		++cnt;
		vv[i+n].pb({fb[i]+n,cnt});
		vv[fb[i]+n].pb({i+n,cnt});
	}
	++cnt;
	vv[ra].pb({rb+n,cnt});
	vv[rb+n].pb({ra,cnt});
	dfs(1);
	cout<<"POSSIBLE"<<endl;
	rp(i,n)cout<<res[i]<<" ";
	cout<<endl;
	return 0;
}
//Crayan_r
posted @ 2023-04-20 14:50  jucason_xu  阅读(12)  评论(0编辑  收藏  举报