51nod 石子分配

image

可以发现步数限制把数轴变为了环。环之间不可以交换,环内相邻两点可以交换,然后我们只需要对每个环操作,最后累加。

对于环上的每个石子堆,我们需要将其石子数调整到均值 \(avg\)。因此,我们首先计算每个堆石子相对于 \(avg\) 的偏差,即 \(nowa[i] - avg\)

因为相邻节点不一定能凑齐 \(avg\),所以我们用 \(b[]\) 数组累积偏差,累积这些偏差后,我们需要选择一个调整量,使得调整所需的总代价最小,那这个数肯定是中位数啊,所以我们环上的每个 \(b[i]\) 都减去中位数然后累加就是这个环的总的操作次数了。

完整代码有注释:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,k; 
ll ans;
ll avg;//平均值 
ll b[50010];//记录累积偏差值
int nowa[50010];//这个环的第几的点 
int a[50010];//这堆石头数 
bool vis[50010];//判断这个点是否在环内 
ll get_ans(int x){
	int t=0;
	while(!vis[x]){//已经在环中了 
		vis[x]=1;
		nowa[++t]=a[x];
		x+=k;
		if(x>n){
			x-=n;
		}
	}
	b[1]=0;
	for(int i=2;i<=t;i++){
		b[i]=b[i-1]+(nowa[i-1]-avg);//累计偏移量 
	}
	sort(b+1,b+1+t);
	ll s1=b[(t+1)/2];//取中位数 
	ll nowans=0;
	for(int i=1;i<=t;i++){
		nowans+=abs(b[i]-s1);//取最小操作数 
	}
	return nowans;
}
int main() {
    ios::sync_with_stdio(false);
    cin>>n>>k;
    k++;
    for(int i=1;i<=n;i++){
    	cin>>a[i];
    	avg+=a[i];
	}
    avg/=n;
    for(int i=1;i<=n;i++){
    	if(vis[i]){//已经在环中了 
    		continue;
		}
		ans+=get_ans(i);
	}
    cout<<ans;
    return 0;
}

posted @ 2024-09-08 16:59  sad_lin  阅读(17)  评论(0)    收藏  举报