[Noip2005] 篝火晚会

题目描述

佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有nnn个同学,编号从111到nnn。一开始,同学们按照1,2,…,n1,2,…,n1,2,,n的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。

佳佳可向同学们下达命令,每一个命令的形式如下:

(b1,b2,...bm−1,bm)(b_1, b_2,... b_{m-1}, b_m)(b1,b2,...bm1,bm)

这里mmm的值是由佳佳决定的,每次命令mmm的值都可以不同。这个命令的作用是移动编号是b1,b2,…,bmb_1,b_2,…, b_mb1,b2,,bm的这m个同学的位置。要求b1b_1b1换到b2b_2b2的位置上,b2b_2b2换到b3b_3b3的位置上,……,要求bmb_mbm换到b1b_1b1的位置上。执行每个命令都需要一些代价。我们假定如果一个命令要移动mmm个人的位置,那么这个命令的代价就是mmm。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?

输入输出格式

输入格式:

第一行是一个整数n(3≤n≤50000)n(3 \le n \le 50000)n(3n50000),表示一共有nnn个同学。

其后nnn行每行包括222个不同的正整数,以一个空格隔开,分别表示编号是111的同学最希望相邻的两个同学的编号,编号是222的同学最希望相邻的两个同学的编号,……,编号是nnn的同学最希望相邻的两个同学的编号。

输出格式:

一个整数,为最小的总代价。如果无论怎么调整都不能符合每个同学的愿望,则输出−1-11。

输入输出样例

输入样例#1: 复制
4
3 4
4 3
1 2
1 2
输出样例#1: 复制
2

说明

对于30%的数据,n≤1000n \le 1000n1000;
对于全部的数据,n≤50000n \le 50000n50000。

2005提高组第三题

 

 
又被Noip题吊打了。
可以看出最小代价等于不在应该在的位置上的数的个数的最小值。
于是我们可以枚举开始位置,还有两个方向,枚举结束时的目标环,求出不在应该在的位置上的数的最小个数就是答案。
但是这样是$\large O(N^2)$的会T,所以找规律。
我们固定起始序列,任取一个目标序列,求出每一个位置到它应该到的位置的步数,发现,不管目标序列是什么样子,相同的就一直相同,不同的就一直不同。
所以我们把答案变成n-最多的不用动的数量。
然后发现其实距离应到的位置距离相同的最大的数量, 就是最多不用动的数量。
所以这样就可以$\large O(N)$求出。
记得正反都跑一遍。
 

 
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
#define reg register 
inline int read() {
    int res = 0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48), ch=getchar();
    return res;
}
#define N 50005
int n;
int deg[N], ch[N][2];
int L[N], R[N];
int cnt[2][N];
int a[N];

int main() 
{
    n = read();
    for (reg int i = 1 ; i <= n ; i ++)
    {
        ch[i][0] = read(), ch[i][1] = read();
        deg[ch[i][0]]++, deg[ch[i][1]]++;
    }
    for (reg int i = 1 ; i <= n ; i ++) if (deg[i] != 2) return puts("-1"), 0;
    int p = 1, num = 0;
    L[1] = ch[1][0], R[1] = ch[1][1];
    do {
        int y = R[p];
        L[y] = p, R[y] = ch[y][0] == p ? ch[y][1] : ch[y][0];
        p = y;
        num++;
    }while(p != 1);
    if (num != n) return puts("-1"), 0;
    p = 1;
    for (reg int i = 1 ; i <= n ; i ++, p = R[p]) 
        cnt[0][(i-p+n)%n]++;
    p = 1;
    for (reg int i = 1 ; i <= n ; i ++, p = L[p]) 
        cnt[1][(i-p+n)%n]++;
    int ans = 0;
    for (reg int i = 0 ; i < n ; i ++)
        ans = max(ans, max(cnt[0][i], cnt[1][i]));
    printf("%d\n", n - ans);
    return 0;
}

 

posted @ 2018-09-03 17:04  zZhBr  阅读(773)  评论(0编辑  收藏  举报