当前位置:   article > 正文

CodeForces1477D Nezzar and Hidden Permutations(构造+调整+菊花图)_菊花图(编程)

菊花图(编程)

problem

洛谷链接

题意:给定 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 两个排列。

solution

我们考虑将每条 ( u , v ) (u,v) (u,v) 限制转化成图 G G G 中的一条边。

如果某个点 i i i 的边数 = n − 1 =n-1 =n1,则一定有 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 <n1 的点了。

反转边的定义, G ˉ = E − G \bar{G}=E-G Gˉ=EG,如果 ( 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,...,avx1=l+x1,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,...,bvx1=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)

code

#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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号