「AGC018F」Two Trees
题目
点这里看题目。
给定两棵树 ,两棵树均包含 个结点。结点编号均从 。
现在需要给每个编号分配一个权值,使得两棵树上的任意子树内,所有的结点编号对应的权值之和都为 或 。
构造任意一种方案,或声明无解。
所有数据满足 。
分析
实际上,找到“子树和”的方案和找到“结点编号”的方案是完全等价的。前者的约束更紧,所以我们可以从“子树和”入手。
设 分别为 树上 的儿子集合, 分别为 树上 的子树和,而 分别为 树的根。则我们相当于要构造 满足:
如果对模型足够熟悉的话,其实现在已经可以看出来约束描述了一个“欧拉回路”的整数线规,因为:
- 每个变量恰好出现在两条限制中,且符号不同,这相当于一条有向边,向两个结点分别贡献入度和出度。
- 每个变量的取值为 ,这相当于是进行“定向”。
- 每条限制为 ,这相当于是要求“入度”和“出度”相等。
所以按照这个线规说的建图即可,可以 判断解的存在性和构造解。
如果认不得模型怎么办?我们做一点变换。设 ,则原线规变成:
此时发现有解的必要条件为 ,也就是欧拉回路存在的必要条件(在图连通时就是充要的)。另一方面,当我们把图建出来之后,我们发现它的结构神似“混合图欧拉回路”模型,并且实际上所有初始边都是无向边(比如,你可以通过 这样的常数推测)。所以,还原回去就可以得到欧拉回路的算法。
Remark.
实际上,在容量全是 的图上 Dinic 复杂度为 ,所以如果没看出来直接跑网络流可能是可以过的。
代码
#include <cstdio>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
const int INF = 1e9;
const int MAXN = 2e5 + 5;
template<typename _T>
inline void Read( _T &x ) {
x = 0; char s = getchar(); bool f = false;
while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
if( f ) x = -x;
}
template<typename _T>
inline void Write( _T x ) {
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) Write( x / 10 );
putchar( x % 10 + '0' );
}
template<typename _T>
inline _T Min( const _T &a, const _T &b ) {
return a < b ? a : b;
}
struct Edge {
int to, nxt;
} Graph[MAXN << 1];
int dir[MAXN];
int head[MAXN], deg[MAXN], cnt = 1;
int S[MAXN], T[MAXN], X[MAXN];
int A[MAXN], B[MAXN];
int N;
inline void AddEdge( const int &from, const int &to ) {
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
head[from] = cnt;
}
void DFS( const int &u ) {
for( int &i = head[u], id ; i ; i = Graph[i].nxt )
if( ! dir[id = i >> 1] ) {
dir[id] = i & 1 ? -1 : 1;
DFS( Graph[i].to );
}
}
int main() {
Read( N );
rep( i, 1, N ) {
Read( A[i] );
if( A[i] == -1 ) A[i] = N + 1;
AddEdge( i, A[i] ), AddEdge( A[i], i );
deg[A[i]] ++;
}
rep( i, 1, N ) {
Read( B[i] );
if( B[i] == -1 ) B[i] = N + 1;
AddEdge( i, B[i] ), AddEdge( B[i], i );
deg[B[i]] ++;
}
rep( i, 1, N )
if( deg[i] & 1 )
return puts( "IMPOSSIBLE" ), 0;
puts( "POSSIBLE" );
DFS( 1 );
rep( i, 1, N ) X[i] = S[i] = dir[i];
rep( i, 1, N ) if( A[i] <= N ) X[A[i]] -= S[i];
rep( i, 1, N ) Write( X[i] ), putchar( " \n"[i == N] );
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2022-01-15 「CF923F」Public Service
2022-01-15 「AGC027D」Modulo Matrix
2021-01-15 [AGC035E] Develop