[LuoguP1053][Noip2005]篝火晚会

现在你有一个排成一个圈的\(N\)大小的队列,一开始的顺序是\(\{1,2,3,4...N\}\),一共有\(N\)个要求,第\(i\)个要求包含两个数\(L[i]\)\(R[i]\),表示\(i\)的左右两个点的编号要是\(L[i]\)\(R[i]\)。现在你可以将原始队列进行操作,即选择几个点\(\{B_1, B_2...B_M\}\)个点,使之依次向右移动一个单位,而\(B_M\)移动到\(B_1\)位置。(\(B\)不一定连续)。每次的移动都需要花费\(M\)的代价。求最小代价使得满足所有要求。若不行则输出\(-1\)

最开始我们要判断是不是要输出\(-1\),很显然如果将所有的要求连边(\(Link(i, L[i]) ; Link(i, R[i])\)),核发情况就会构成一个环。如果构不成环那么肯定不合法,就输出\(-1\)。那么我们要做的就是根据\(L[i]\)\(R[i]\)进行一遍搜索,然后立个\(Vis[i]\),在搜索完之后我们进行一遍判断,如果所有的\(Vis[i]\)都等于\(1\),那么久合法,否则就不合法。

inline void Dfs(int Now) {
    F[Now] = 1 ;
    if (! F[L[Now]]) Dfs(L[Now]) ;
    else if (! F[R[Now]]) Dfs(R[Now]) ;
    else return ;
}
    Dfs(1) ;
    for (int i = 1 ; i <= N ; i ++) if (! F[i]) {
        puts("-1") ; return 0 ; 
    }

判断合法之后我们再进行下面的东西。

首先发现这个环很难受于是我们把它变成链。

inline void Build_Chain(int Now) {
    Line[++ Sum] = Now ; V[Now] = 1 ;
    if (! V[L[Now]]) {
        Build_Chain(L[Now]) ;
        return ;
    }
    if (! V[R[Now]]) {
        Build_Chain(R[Now]) ;
        return ;
    }
    return ;
}

然后对于样例我们就建出来了一条\(\{1 ~4 ~2~ 3\}\)的链。

然后我们考虑最优的方案肯定是对于每一次的序列只操作与目标链的位置不相同的点。拿样例为例子。

\(\{1 ~4 ~2~ 3\}\)\(\{1 ~2 ~3~ 4\}\)比,我们发现\(\{4, 2, 3\}\)的位置都不一样,于是操作\(\{4, 2, 3\}\),变为\(\{1 ~3 ~4 ~2\}\)。然后我们发现还是这三个数不一样,于是继续操作就变成了目标序列。

发现不大一样啊,然后我们发现这条链的建立顺序是先左后右的顺序,因此我们尝试再次建立一个先右后左的顺序的到的链就是\(\{1~3~2~4\}\),再次操作发现就得到了答案\(2\)。于是我们知道一条链的出的结果不一定最优,我们要建两条链。然后取答案的最优值。

因此我们可以看到答案的雏形就是对于每一次操作的新序列,如果有\(A\)个相同的单位点,也就是有\(N- A\)个不相同的,那么代价就是\(N - A\)

然后可以进一步优化发现对于每一个元素,我们记录一下它和目标序列的差值为\(T[i]\),然后我们寻找所有的\(N\)\(T[i]\),对于每一个相同\(T[i]\)\(i\)来说它们是等价的,因为都可以通过\(T[i]\)次换到目标点。于是我们记录两条链的最大相同\(T[i]\)数,然后用\(N\)一减就是答案。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std ;
typedef long long LL ;
const int MAXN = 50100 ;
int N, Line[MAXN], Sum, Ans, V[MAXN], F[MAXN], L[MAXN], R[MAXN], Line2[MAXN], Sum2 ;
int T[MAXN] ;

inline int Read() {
    int X = 0, F = 1 ; char ch = getchar() ;
    while (ch > '9' || ch < '0') F = (ch == '-' ? - 1 : 1), ch = getchar() ;
    while (ch >= '0' && ch <= '9') X=(X<<1)+(X<<3)+(ch^48), ch = getchar() ;
    return X * F ;
}

inline void Dfs(int Now) {
    F[Now] = 1 ;
    if (! F[L[Now]]) Dfs(L[Now]) ;
    else if (! F[R[Now]]) Dfs(R[Now]) ;
    else return ;
}

inline void Build_Chain(int Now) {
    Line[++ Sum] = Now ; V[Now] = 1 ;
    if (! V[L[Now]]) {
        Build_Chain(L[Now]) ;
        return ;
    }
    if (! V[R[Now]]) {
        Build_Chain(R[Now]) ;
        return ;
    }
    return ;
}

inline void Build_Chain2(int Now) {
    Line2[++ Sum2] = Now ; V[Now] = 1 ;
    if (! V[R[Now]]) {
        Build_Chain2(R[Now]) ;
        return ;
    }
    if (! V[L[Now]]) {
        Build_Chain2(L[Now]) ;
        return ;
    }
    return ;
}

int main() {
    N = Read() ;
    for (int i = 1 ; i <= N ; i ++) {
        int X = Read(), Y = Read() ;
        L[i] = X ; R[i] = Y ;
    }
    Dfs(1) ;
    for (int i = 1 ; i <= N ; i ++) if (! F[i]) {
        puts("-1") ; return 0 ;
    }
    Build_Chain(1) ;
    memset(V, 0, sizeof(V)) ;
    Build_Chain2(1) ;
    for (int i = 1 ; i <= N ; i ++) {
        int K = Line[i] - i ;
        K = (K + N) % N ;
        T[K] ++ ;
    }
    for (int i = 1 ; i <= N ; i ++) Ans = max(Ans, T[i]) ;
    memset(T, 0, sizeof(T)) ;
    for (int i = 1 ; i <= N ; i ++) {
        int K = Line2[i] - i ;
        K = (K + N) % N ;
        T[K] ++ ;
    }
    for (int i = 1 ; i <= N ; i ++) Ans = max(Ans, T[i]) ; 
    printf("%d", N - Ans) ; 
    return 0 ;
}

转载于:https://www.cnblogs.com/Yeasio-Nein/p/P1053.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值