「AGC018F」Two Trees

题目

点这里看题目。


给定两棵树 A,B,两棵树均包含 n 个结点。结点编号均从 1n

现在需要给每个编号分配一个权值,使得两棵树上的任意子树内,所有的结点编号对应的权值之和都为 11

构造任意一种方案,或声明无解。

所有数据满足 1n105

分析

实际上,找到“子树和”的方案和找到“结点编号”的方案是完全等价的。前者的约束更紧,所以我们可以从“子树和”入手。

Pu,Qu​ 分别为 A,B​ 树上 u​ 的儿子集合,su,tu 分别为 A,B 树上 u 的子树和,而 r1,r2 分别为 A,B 树的根。则我们相当于要构造 s,t 满足:

{sr1=tr2suvPusv=tuvQutv1unsu,tu{1,1}1un

如果对模型足够熟悉的话,其实现在已经可以看出来约束描述了一个“欧拉回路”的整数线规,因为:

  1. 每个变量恰好出现在两条限制中,且符号不同,这相当于一条有向边,向两个结点分别贡献入度和出度。
  2. 每个变量的取值为 {1,1}​,这相当于是进行“定向”。
  3. 每条限制为 =0,这相当于是要求“入度”和“出度”相等。

所以按照这个线规说的建图即可,可以 O(n)​ 判断解的存在性和构造解。


如果认不得模型怎么办?我们做一点变换。设 su=2xu1,tu=2yu1,则原线规变成:

max0s.t.xr1yr2=0xu+vQuyv=yu+vPuxv+12(|Qu||Pu|)1unxu,yu{0,1}1un

此时发现有解的必要条件为 1un,|Pu||Qu|(mod2),也就是欧拉回路存在的必要条件(在图连通时就是充要的)。另一方面,当我们把图建出来之后,我们发现它的结构神似“混合图欧拉回路”模型,并且实际上所有初始边都是无向边(比如,你可以通过 12(|Qu||Pu|) 这样的常数推测)。所以,还原回去就可以得到欧拉回路的算法。

Remark.

实际上,在容量全是 1 的图上 Dinic 复杂度为 O(m×min{n23,m12}),所以如果没看出来直接跑网络流可能是可以过的。​

代码

#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;
}
posted @   crashed  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需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
点击右上角即可分享
微信分享提示