dtoj#4140. 搬砖(banzhuan)

题目描述:

为什么前两题中没有出现小 G ? 因为小 G 去工地搬砖了。

现在小 G 面前从左到右放了 $n$ 块砖,第 $i$ 块砖重 $a_i$ 斤。小 G 的任务就是把这些砖头从左到右按重量从小到大排好顺序。如果说小 G 完成任务后从左到右第 $i$ 块砖重 $b_i$ 斤,那么必须满足 $b_i ≤ b_{i+1}$。小 G 身体瘦弱,每次搬运只能做到交换任意两块砖。因为工资是按搬运次数算的,小 G 希望在完成任务前能搬运尽量多次。但是因为有包工头盯着,又不能乱搬。如果一次搬运前有 $x$ 个 $i$ 满足 $a_i = b_i$,而搬运后有 $y$ 个 $i$ 满足 $a_i = b_i$ , $y$ 就必须大于 $x$,否则包工头就会发现小 G 故意不好好干然后把小 G 炒了。现在小 G 想知道他完成任务时最多搬运了几次。

思路:

对于 $ai!=bi$ 的点我们把 $ai$ 和 $bi$ 相连,

此时构成的联通块里如果有 $2$ 个种颜色的点,那么就会有点数 $/2$ 的贡献,因为每次只能两两不同的交换位置。

如果有大于 $2$ 个种颜色,那么每次可以选择仅让一个归回原位,直到只剩下两个点,不得不至多只有两种颜色时,答案为 $1$ ,所以这种情况贡献为点数 $-1$ 。

以下代码:

#include<bits/stdc++.h>
#define il inline
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e6+5;
int n,tot,fa[N],a[N],b[N],num[N],ans,sum[N];
map<int,int> m;
il int read(){
    int x,f=1;char ch;
    _(!)ch=='-'?f=-1:f;x=ch^48;
    _()x=(x<<1)+(x<<3)+(ch^48);
    return f*x;
}
il int getfa(int x){
    if(!fa[x])return x;
    return fa[x]=getfa(fa[x]);
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)a[i]=b[i]=read();
    sort(b+1,b+1+n);
    m[b[1]]=++tot;
    for(int i=2;i<=n;i++)if(b[i]!=b[i-1])m[b[i]]=++tot;
    for(int i=1;i<=n;i++){
        if(a[i]!=b[i]){
            int x=getfa(m[a[i]]),y=getfa(m[b[i]]);
            if(x==y)continue;
            fa[x]=y;
        }
    }
    for(int i=1;i<=n;i++){
        if(a[i]!=b[i]){
            sum[getfa(m[a[i]])]++;
        }
    }
    for(int i=1;i<=tot;i++)num[getfa(i)]++;
    for(int i=1;i<=tot;i++){
        if(num[i]>2)ans+=max(sum[i]-1,0);
        else ans+=sum[i]/2;
    }
    printf("%d\n",ans);
    return 0;
}
View Code

 

posted @ 2019-02-22 07:42  Jessiejzy  阅读(377)  评论(0编辑  收藏  举报