[NOIP提高组2005]篝火晚会

来源:互联网 发布:手机淘宝自动秒杀软件 编辑:程序博客网 时间:2024/05/16 01:54

【问题描述】

 

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

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

(b1, b2,... bm -1, bm)

这里m的值是由佳佳决定的,每次命令m的值都可以不同。这个命令的作用是移动编号是b1,b2,…… bm –1,bm的这m个同学的位置。要求b1换到b2的位置上,b2换到b3的位置上,……,要求bm换到b1的位置上。

执行每个命令都需要一些代价。我们假定如果一个命令要移动m个人的位置,那么这个命令的代价就是m。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?

对于30%的数据,n <= 1000;

对于全部的数据,n <= 50000。


【输入文件】

输入的第一行是一个整数n(3 <= n <= 50000),表示一共有n个同学。其后n行每行包括两个不同的正整数,以一个空格隔开,分别表示编号是1的同学最希望相邻的两个同学的编号,编号是2的同学最希望相邻的两个同学的编号,……,编号是n的同学最希望相邻的两个同学的编号。
【输出文件】

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


题解:

我们可以发现,由初始序列我们可以得到2*n个目标序列,

将目标序列与初始序列做差,如果差相同则不用移动,所以我们只要枚举 出最大的相同数,那么ans就是ans-max

但这样会超时。

只要求出目标环各元素值与初始环对应位置的元素值之差的结果相同的最多是多少个就可以了

为使差值为正数,可用用 (a[i]-b[i]+n)%n来求差值(b[i]表示初始序列,实际就是12345……n)

同时注意因为是一个环,那么与方向无关,所以顺时针,逆时针我们都用上述方法求解一次

#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>using namespace std;const int N=110010;struct node{int y,next;}sa[N*2];int len,first[N];int n;void ins(int x,int y){for (int i=first[x];i!=-1;i=sa[i].next) if(y==sa[i].y) return;len++;sa[len].y=y;sa[len].next=first[x];first[x]=len;}int sd[N];int flag=0;void init(){for(int num=0,x=1;x<=n;x++,num=0){for(int i=first[x];i!=-1;i=sa[i].next)num++;if(num!=2){printf("-1");flag=1;return;}}int now=1,f=0;for(int x=1;x<=n;x++){sd[x]=now;for(int i=first[now];i!=-1;i=sa[i].next){int y=sa[i].y;if(y!=f){f=now;now=y;break;}}}}int yu[N];int ans=0;void work(){memset(yu,0,sizeof(yu));for(int i=1;i<=n;i++){int o=i-sd[i];if(o<0) o+=n;yu[o]++;ans=max(yu[o],ans);}memset(yu,0,sizeof(yu));for(int i=1;i<=n;i++){int o=i-sd[n-i+1];if(o<0) o+=n;yu[o]++;ans=max(yu[o],ans);}printf("%d",n-ans);}int main(){scanf("%d",&n);memset(first,-1,sizeof(first));for(int i=1;i<=n;i++){int l,r;scanf("%d%d",&l,&r);ins(i,l);ins(l,i);ins(i,r);ins(r,i);}init();if(flag!=1)work();}


原创粉丝点击