AtCoder Beginner Contest 268-E题解

题目大意

\(n\)个人,编号为\(0,1,2,3,\cdots,n-1\),围成一个圈,有一个排列,表示在第\(i\)个人手中的数是\(P_i\)

每次操作,第\(i\)个人把手中的数给\((i+1)mod\ n\)。第\(i\)个人的不满意度为\(i\)这个数离他的距离。

问不满意度之和最小为多少

题解

分析

由于是绕圈着的,所以第\(i\)人离\(i\)这个数的距离可以描述为两种状态

  1. \(i\)这个数逐渐离\(i\)这个人靠近
  2. \(i\)这个数逐渐离\(i\)这个人远离

由于所有数的移动速度都为\(1\),所以所有点的这两个状态都满足先进先出的原则。

所以我们可以用两个队列来维护处在这两种状态下的数。

记得处理一开始的入队顺序便可以通过本题。

code

#include<bits/stdc++.h>
#define rg register
#define fre(x)freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;
typedef long long LL;
typedef long double LD;
typedef double db;
typedef unsigned long long uLL;
const db Pi=acos(-1);
queue<int> q[2];
vector<pair<int,int> > p[2];
int a[200005];
int main(){
    LL now=0,ans;
    int n;scanf("%d",&n);
    for(int i=0,x;i<n;i++){
        scanf("%d",&x);
        int l=(x-i+n)%n;
        int r=(i-x+n)%n;
        p[l>r].push_back({min(l,r),x});
        now+=min(l,r),a[x]=i;
    }
    sort(p[0].begin(),p[0].end());
    for(int i=0;i<p[0].size();i++)
    q[0].push(p[0][i].second);
    sort(p[1].rbegin(),p[1].rend());
    for(int i=0;i<p[1].size();i++)
    q[1].push(p[1][i].second);
    ans=now;
    for(int i=1;i<n;i++){
        while(!q[0].empty()){
            int u=q[0].front();
            int v=(a[u]+i)%n;
            int l=(u-v+n)%n;
            int r=(v-u+n)%n;
            if(l<=r)break;
            q[0].pop(),q[1].push(u);
        }
        while(!q[1].empty()){
            int u=q[1].front();
            int v=(a[u]+i)%n;
            int l=(u-v+n)%n;
            int r=(v-u+n)%n;
            if(r<l)break;
            q[1].pop(),q[0].push(u);
            l=(l+1)%n,r=(r+n-1)%n;
            now-=r,now+=l;
        }
        now-=q[0].size(),now+=q[1].size();
        ans=min(ans,now);
    }printf("%lld",ans);
    system("pause");
    return 0;
}
posted @ 2022-09-10 22:48  董哲仁  阅读(186)  评论(0)    收藏  举报