P1053 篝火晚会

Luogu 链接

题意

题目描述

现有 \(n\) 个人,编号为 \(1\)\(n\)。一开始,他们按 \(1,2,\dots,n\) 的顺序排成一圈。然而,每个人都有其最希望相邻的两个人。

我们可以执行以下操作若干次,使得最后排列的顺序能满足每个人的要求:

  • 给定一个序列 \((b_1,b_2,\cdots,b_{m-1},b_m)\),并让 \(b_1\) 换到 \(b_2\) 的位置上,\(b_2\) 换到 \(b_3\) 的位置上,……,\(b_{m-1}\) 换到 \(b_m\) 的位置上,\(b_m\) 换到 \(b_1\) 的位置上。这一操作的代价为 \(m\),并且每次操作的 \(m\) 可以不相等。

注意:移动的人不一定要连续。

若能通过若干次操作,使得最后的顺序能满足每个人的要求,则输出最小总代价;否则输出 \(-1\)


输入格式

第一行一个正整数 \(n\)\(3\le n\le5\times10^4\)),含义见题目描述

之后的 \(n\) 行每行都包括两个正整数 \(A_i\)\(B_i\)\(1\le A_i,B_i\le n\)\(A_i\neq B_i\)),表示编号为 \(i\) 的人最希望与之相邻的两个人的编号。


输出格式

题目描述

思路

首先,根据每个人的希望,我们可以先尝试构造出一条目标链,如果构造不出来,则可以直接输出 \(-1\)

然后,我们将目标链与初始链对应的数作差(模 \(n\) 的意义下),那些结果为 \(0\) 的人的位置是不用变的,而剩下的人都需要调整。

接着我们便可以惊奇的发现,若需要调整的人数为 \(m\),则我们只需要 \(1\) 次代价为 \(m\) 的操作即可将他们调整好 (至于为什么则留给读者思考)

当然由于环是可以旋转的,所以实际上我们不需要调整的人不一定就是作差后结果为 \(0\) 的人。那么,选择哪些人可以使我们操作的总代价最小呢?

为了使调整的人数最少,我们无需调整的人数就要最多。而实际上,只要我们采取合适的旋转方式,任何一个人都可以由需要调整,变为不需要调整。因此,我们只需统计在作差得到的结果中出现次数最多的数(即众数)出现了几次,而这就是无需调整的人数。

最后,由于最初的顺序是顺时针还是逆时针不确定,所以我们需要二者都跑一遍,最后得出结果。

例如,对于样例,模拟结果如下:

  • 构造目标链:\(1,4,2,3\)
  • 构造初始链:\(1,2,3,4\)
  • 顺时针统计:\(0,2,3,3\)
  • 逆时针统计:\(1,1,0,2\)
  • 无需调整的人数:\(2\)
  • 最终结果:\(4-2=2\)

因此输出结果为:

2

程序

AC 记录

#include<cstdlib>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cstdio>
#include<iostream>
#include<vector>
#include<map>
#include<cmath>
#include<iomanip>
#include<string>
#include<stack>
using namespace std;
long long T=1,mod;
#define ll long long
#define ull unsigned long long
#define LL 2e18
#define INT 1e9
#define INF 0x3f3f3f3f
#define MAX int(50000)
//#define DEBUG
//#define use_cin
//#define more_text

int n,                       //人数 
	expect_person[MAX+1][2]; //每个人希望相邻的两个人 

int target_list[MAX+1],      //目标链 
	initial_list[MAX+1],     //初始链 
	plus_order[MAX+1],       //顺时针,统计作差结果出现的次数 
	minus_order[MAX+1],      //逆时针,统计作差结果出现的次数 
	ans;                     //记录无需调整的人数的最大值 

void solve(int step){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%d%d",&expect_person[i][0],&expect_person[i][1]);//输入 
	
	target_list[1]=1;
	target_list[2]=expect_person[1][1];
	for(int i=2;i<=n-1;++i){
		if(target_list[i-1]==expect_person[target_list[i]][0])
			target_list[i+1]=expect_person[target_list[i]][1];
		else if(target_list[i-1]==expect_person[target_list[i]][1])
			target_list[i+1]=expect_person[target_list[i]][0];
		else{
			printf("-1"); //无法满足要求 
			return;
		}
	} //构造目标链 
	
	for(int i=1;i<=n;++i)initial_list[i]=i; //构造初始链 
	
	for(int i=1;i<=n;++i)
		++plus_order[(target_list[i]-initial_list[i]+n)%n],      //顺时针统计
		++minus_order[(target_list[i]-initial_list[n-i+1]+n)%n]; //逆时针统计
	
	for(int i=0;i<n;++i)
		ans=max(ans,max(plus_order[i],minus_order[i])); //统计答案 
	
	printf("%d",n-ans); //输出 
}
int main(){//模板,不用在意
	#ifdef DEBUG
	freopen("test.in","r",stdin);freopen("test.out","w",stdout);
	#endif
	#ifdef more_text
	#ifdef use_cin
	cin>>T;
	#else
	scanf("%lld",&T);
	#endif
	#endif
	for(int i=0;i<T;++i)solve(i);
	#ifdef DEBUG
	fclose(stdin);fclose(stdout);
	#endif
	return 0;
}
/*
Input:

Output:

Outline:

*/
posted @ 2025-03-09 09:47  LXcjh4998  阅读(18)  评论(0)    收藏  举报