大江东去,浪淘尽,千古风流人物。故垒西边,人道是,三国周郎赤壁。乱石穿空,惊涛拍岸,卷起千堆雪。江山如画,一时多少豪杰。遥想公瑾当年,小乔初嫁了,雄姿英发。羽扇纶巾,谈笑间,樯橹灰飞烟灭。故国神游,多情应笑我,早生华发。人生如梦,一尊还酹江月。

Noip2005 篝火晚会

模拟+思维

05年的题,然而我却不会做

好多题解都没有证明为什么如果有m个人位置错误,只需花费m的代价就可以将其变为正确,我来证明一下

这m个人可以看成一个错排,每个人都有其应该在的位置p[i],从i向p[i]连一条边,则整张图中至少有一个环

因为对于每个点i,显然有一条出边和入边,故所有点都在环上,故只需把这个环整体移动一次

大佬都是直接模拟建图,只有我非常垃圾地用了dfs判基环的方法,代码又臭又长

我们的目标是找到一个x,使得整个环右移x位后,在目标位置的人最多

环可以反着看,因此需要把目标位置reverse一下再做一次

#include<bits/stdc++.h>
using namespace std;

#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define fo(i,a) for(int i=0;i<a;++i)
#define il inline

const int inf=0x3f3f3f3f,N=5e4+10;

int n,p[N],s[N],top=0,tot,head[N],cnt;
int c1[N],c2[N],mx;
bool vis[N];
struct edge{
	int nxt,v;
}e[N*2];

void add(int u,int v){
	e[cnt]=(edge){head[u],v};
	head[u]=cnt++;
}

il void read(int &x){
	x=0;char c=getchar(),f=1;
	while(c<'0'||c>'9'){ c=='-'?f=-1:0; c=getchar(); }
	while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); }
	x*=f;
}

il void fail(){
	puts("-1");exit(0);
}

bool dfs(int u,int fa){
	if(vis[u]){
		int x;
		do{
			x=s[top--];
			p[++tot]=x;
		}while(x!=u&&top);
		if(tot!=n) fail();
		else return 1;
	}
	vis[u]=1;
	s[++top]=u;
	for(int i=head[u];i+1;i=e[i].nxt){
		int v=e[i].v;
		if(v==fa) continue;
		if(dfs(v,u)) return 1;
	}
	--top;
	return 0;
}

int main(){
	//freopen("input.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	mem(head,-1);
	read(n);
	int x,y;
	go(i,1,n){
		read(x),read(y);
		add(i,x),add(i,y);
	}
	dfs(1,0);
	go(i,1,n){
		mx=max(mx,++c1[(p[i]-i+n)%n]);
		mx=max(mx,++c2[(i-(n-p[i]+1)+n)%n]);
	}
	cout<<n-mx;
	return 0;
}
posted @ 2019-11-15 09:53  White_star  阅读(133)  评论(0编辑  收藏  举报
}