赞
踩
题意:给定 m m m 条形如 ( u , v ) (u,v) (u,v) 的限制,要求 a u , a v a_u,a_v au,av 的相对大小关系与 b u , b v b_u,b_v bu,bv 相同。
且尽可能减少 a i = b i a_i=b_i ai=bi 的数量,输出 a , b a,b a,b 两个排列。
我们考虑将每条 ( u , v ) (u,v) (u,v) 限制转化成图 G G G 中的一条边。
如果某个点 i i i 的边数 = n − 1 =n-1 =n−1,则一定有 a i = b i a_i=b_i ai=bi。( n n n 为当前剩余点的数量)
边数等于点数减一,这说明点 i i i 与其它所有点都有一定的要求,假设要求 b i > b k b_i>b_k bi>bk 的 k k k 有 x x x 个,则 a i > a k ′ a_i>a_k' ai>ak′ 的 k ′ k' k′ 也等于 x x x。
这种确定的点直接按死不参与后面的讨论了。
那么到这里就剩下了一堆边数 < n − 1 <n-1 <n−1 的点了。
反转边的定义, G ˉ = E − G \bar{G}=E-G Gˉ=E−G,如果 ( u , v ) (u,v) (u,v) 在 G ˉ \bar{G} Gˉ 为一条边当且仅当 ( u , v ) (u,v) (u,v) 原来是没有限制的。
这些点都是没有满边的,所以一定会跟至少一个其它点连边的。
G ˉ \bar{G} Gˉ 形成的是一个生成树森林,没有必要多连边形成图,独立是传递的,所以 G ˉ \bar{G} Gˉ 是多样的。
现在连边的两个点之间的选择是互不干涉的。
考虑一个特殊的情况——两层的菊花图。有一个非常简单的构造。
假设菊花图的根为 u u u,所有叶子依次为 v 1 , v 2 , . . . , v x v_1,v_2,...,v_x v1,v2,...,vx。
对于 a a a 序列, a u = l , a v 1 = l + 1 , . . . , a v x − 1 = l + x − 1 , a v x = l + x a_u=l,a_{v_1}=l+1,...,a_{v_{x-1}}=l+x-1,a_{v_x}=l+x au=l,av1=l+1,...,avx−1=l+x−1,avx=l+x。
对于 b b b 序列, b u = l + 1 , b v 1 = l + 2 , . . . , b v x − 1 = l + x , b x v = l b_u=l+1,b_{v_1}=l+2,...,b_{v_{x-1}}=l+x,b_{x_v}=l bu=l+1,bv1=l+2,...,bvx−1=l+x,bxv=l。
用同样的数字区间 [ l , l + x ] [l,l+x] [l,l+x] 填写了 a , b a,b a,b 同样的点集,且打了个等差 1 1 1。
这样子,内部反正是没有限制,不用管数字间的大小,也没有产生多余的 a i = b i a_i=b_i ai=bi 情况数。
而与外部部分,因为都是用的连续段数字,相对关系也是满足的。
例如有个点 t t t 与内部点有相对大小关系的限制,内部用的是 [ l , l + x ] [l,l+x] [l,l+x] , t t t 所在点无论是 [ l ′ , l ) / ( l + x , r ′ ] [l',l)/(l+x,r'] [l′,l)/(l+x,r′], a , b a,b a,b 都是一致的。要么都小于最小值,要么都大于最大值。
将 G ˉ \bar{G} Gˉ 中一些边断掉是不会出错的(因为这反而相当于外加了一些限制)。
所以,我们可以就将 G ˉ \bar{G} Gˉ 直接断成若干个两层菊花图。
换言之,最后的答案最少个数可以通过调整构造变成最开始确定的点的数量。
有很多种裂成不同菊花图的方法,下面是官方做法:
枚举未分配的节点 u u u。
如果 u u u 有临边点 v v v 未分配,将 u u u 和所有未分配的 v v v 一起构成一个新的菊花图,并以 u u u 为根。
否则,说明 u u u 的所有邻居均已有过分配的菊花图。随便选一个邻居 v v v。
如果 v v v 所在的菊花图至少有 3 3 3 个点,那么就可以把 v v v 割裂出来,使得 u , v u,v u,v 成为新的菊花图。
注意: v v v 此时不可能是其所在菊花图的根,如果是那么 u u u 早就隶属与 v v v 为根的那个菊花图了。
否则,说明 v v v 所在的菊花图只有两个点,将 u u u 加入那个菊花图,并将那个菊花图转变为以 v v v 为根即可。
G ˉ \bar{G} Gˉ 中可存在的边很多, n 2 n^2 n2 级别的,但不一定是全都必须出现的。怎么快速求出想要的生成树?
用两个 set
维护已出现在
G
ˉ
\bar{G}
Gˉ 中的点和还未出现的点,在 dfs-tree
的时候顺便加边构出生成树的边。
时间复杂度: O ( ( n + m ) log n ) O((n+m)\log n) O((n+m)logn)。
#include <bits/stdc++.h> using namespace std; #define maxn 500005 set < pair < int, int > > graph; set < int > id, G[maxn], R[maxn]; int id1[maxn], id2[maxn], deg[maxn]; void dfs( int u ) { id.erase( u ); int v = 0; while( 1 ) { auto it = id.upper_bound( v ); if( it == id.end() ) break; v = *it; if( G[u].find( v ) != G[u].end() ) continue; R[u].insert( v ); R[v].insert( u ); dfs( v ); } } int main() { int T, n, m; scanf( "%d", &T ); while( T -- ) { scanf( "%d %d", &n, &m ); for( int i = 1;i <= n;i ++ ) G[i].clear(), R[i].clear(); id.clear(); for( int i = 1;i <= n;i ++ ) id.insert( i ); for( int i = 1, u, v;i <= m;i ++ ) { scanf( "%d %d", &u, &v ); G[u].insert( v ); G[v].insert( u ); } while( id.size() ) dfs( * id.begin() ); int num = n; for( int i = 1;i <= n;i ++ ) { deg[i] = R[i].size(); if( deg[i] ) graph.insert( make_pair( deg[i], i ) ); else id1[i] = id2[i] = num --; } int l = 0, r = 0, u; while( graph.size() ) { u = graph.begin() -> second; u = *R[u].begin(); graph.erase( make_pair( deg[u], u ) ); vector < int > scc; for( int v : R[u] ) { graph.erase( make_pair( deg[v], v ) ); if( deg[v] == 1 ) scc.push_back( v ); else graph.insert( make_pair( -- deg[v], v ) ), R[v].erase( u ); } id1[u] = ++ l; for( int i : scc ) id1[i] = ++ l, id2[i] = ++ r; id2[u] = ++ r; } for( int i = 1;i <= n;i ++ ) printf( "%d ", id1[i] ); puts(""); for( int i = 1;i <= n;i ++ ) printf( "%d ", id2[i] ); puts(""); } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。