📂构造
🔖思维
2021-10-18 21:12阅读: 64评论: 2推荐: 3

[NOIP2005]篝火晚会

篝火晚会

ps:觉得网上的一些题解比较奇怪,看不懂,自己写一篇来说说这道题。

题面

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

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

(b1,b2,...bm1,bm)

注意: b1bm 并不要求为连续数值

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

分析

代价

暴力点想,一个数位置不合法,我想让它合法,假设目前这个数的位置是 x ,在它合法位置上的数是 y ,显然我们就是要交换 xy ,但这样可能会导致 y 也不合法。

发现其实扩展一下我们将 y 的后面添上 z ,满足 y 的合法位置是 z ,这样一直扩展,如果当前有解,肯定会扩展回 x

所以对于 m 个不合法的数,我们只需要代价为 m 进行移动。当然 m 个不合法的数可能不是同一组,但是代价的总和一定是 m

所以代价就是 ncnt ,其中 cnt 表示与原排列符合的数的个数。

优化

然后我们发现我们现在只需要确定目标环,很容易发现目标环其实只有两种,即 1 想要相邻的两个数在不同位置,那么我们断环为链就能拿到两个不同的序列。

这个序列我们又可以将它左移得到新的序列。

不难发现其实每个数只会在第 k 轮左移中与原序列相等,那我们只需要对得到的序列进行预处理,求出 dp[i] 表示第 i 轮中有多少个数不用动。

讨论一下序列当前数到原序列 1,2,34 的距离就好了,然后算一下累计一下贡献。

统计答案当然就是 min(ndp[i])

无解

无解的情况很容易,如果 i 想相邻的数不想和 i 相邻,就无解了。

CODE

#include <bits/stdc++.h>
using namespace std;
const int N=5e4+10,INF=1e9;
const int Mxdt=100000; 
inline char gc(){
	static char buf[Mxdt],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
	int t=0,f=0;char v=gc();
	while(v<'0')f|=(v=='-'),v=gc();
	while(v>='0')t=(t<<3)+(t<<1)+v-48,v=gc();
	return f?-t:t;
}
struct node{ int x,y; }arr[N];
int n,ans;
int cnt,line[N];
int dp[N];
bool vis[N];
int tot,v[4*N],nex[4*N],first[4*N];
inline void Add(int x,int y)
{
	nex[++tot]=first[x];
	first[x]=tot,v[tot]=y;
}
inline void Get_line(int u)
{
	vis[u]=true;
	line[++cnt]=u;
	for(register int i=first[u];i;i=nex[i]){
		int to=v[i];
		if(vis[to]) continue;
		Get_line(to);
	}
}
inline void Solve()
{
	memset(dp,0,sizeof(dp));
	for(register int i=1;i<=cnt;i++) dp[(i+(n-line[i]))%cnt]++;
	for(register int i=0;i<cnt;i++) ans=min(ans,cnt-dp[i]); 
}
int main()
{
	n=read();
	for(register int i=1;i<=n;i++){
		arr[i].x=read(),arr[i].y=read();
		if(arr[i].x<i) //已经输入 
			if(arr[arr[i].x].x!=i&&arr[arr[i].x].y!=i) { puts("-1"); return 0; }
		if(arr[i].y<i)
			if(arr[arr[i].y].x!=i&&arr[arr[i].y].y!=i) { puts("-1"); return 0; } 
		Add(i,arr[i].x),Add(i,arr[i].y);
		Add(arr[i].x,i),Add(arr[i].y,i);
	}
	Get_line(1);
	ans=INF;
	Solve();
	reverse(line+1,line+cnt+1);
	Solve();
	printf("%d\n",ans);
	return 0;
}

本文作者:╰⋛⋋⊱๑落叶๑⊰⋌⋚╯

本文链接:https://www.cnblogs.com/Defoliation-ldlh/p/15422335.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ╰⋛⋋⊱๑落叶๑⊰⋌⋚╯  阅读(64)  评论(2编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起