[NOIP2005]篝火晚会
篝火晚会
ps:觉得网上的一些题解比较奇怪,看不懂,自己写一篇来说说这道题。
题面
佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”。在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会。一共有 个同学,编号从 到 。一开始,同学们按照 的顺序坐成一圈,而实际上每个人都有两个最希望相邻的同学。如何下命令调整同学的次序,形成新的一个圈,使之符合同学们的意愿,成为摆在佳佳面前的一大难题。
佳佳可向同学们下达命令,每一个命令的形式如下:
注意: 并不要求为连续数值
这里 的值是由佳佳决定的,每次命令 的值都可以不同。这个命令的作用是移动编号是 的这 个同学的位置。要求 换到 的位置上, 换到 的位置上,……,要求 换到 的位置上。执行每个命令都需要一些代价。我们假定如果一个命令要移动 个人的位置,那么这个命令的代价就是 。我们需要佳佳用最少的总代价实现同学们的意愿,你能帮助佳佳吗?
分析
代价
暴力点想,一个数位置不合法,我想让它合法,假设目前这个数的位置是 ,在它合法位置上的数是 ,显然我们就是要交换 和 ,但这样可能会导致 也不合法。
发现其实扩展一下我们将 的后面添上 ,满足 的合法位置是 ,这样一直扩展,如果当前有解,肯定会扩展回 。
所以对于 个不合法的数,我们只需要代价为 进行移动。当然 个不合法的数可能不是同一组,但是代价的总和一定是 。
所以代价就是 ,其中 表示与原排列符合的数的个数。
优化
然后我们发现我们现在只需要确定目标环,很容易发现目标环其实只有两种,即 想要相邻的两个数在不同位置,那么我们断环为链就能拿到两个不同的序列。
这个序列我们又可以将它左移得到新的序列。
不难发现其实每个数只会在第 轮左移中与原序列相等,那我们只需要对得到的序列进行预处理,求出 表示第 轮中有多少个数不用动。
讨论一下序列当前数到原序列 的距离就好了,然后算一下累计一下贡献。
统计答案当然就是 。
无解
无解的情况很容易,如果 想相邻的数不想和 相邻,就无解了。
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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步