正题
大意
有k个摊点,有n行m列,每次只能让摊点移动到相邻的格子(如果相邻的有那么就交换)。要求最少的移动次数让每行每列的摊点数一样,如果不行就输出只能做到行或只能做到列或都不行。注意最上面一行和最下面一行相邻,最左边一行和最右边一行相邻。
解题思路
我们先不考虑最上和最左的情况。
我们可以发现列和行其实是不会相互影响的,那么我们可以分开来处理。我们可以发现行可以的情况仅当k%n==0k\%n==0k%n==0而列可行仅当k%m==0k\%m==0k%m==0,然后我们就可以处理第一个输出了。
然后我们会发现这样的话就十分的像均分纸牌,我们选择一个中转行/列,将任何每行/列多余的或缺少的都当做一个东西,然后将所有东西移动到这个中转行/列就可以做到求值了,然后我们要选择最优的中转点的话那么我们取中位数是最优的。
接下来我们可以考虑边界的情况了,我们可以将缺少的前缀和取中位数然后像前面一样求值就是最优的。
原理:
我们其实就是枚举断点然后求中位数,而如果断点是kkk,那么剩下的位置的前缀和:
K/M | 前缀和 |
---|---|
A[k+1]A[k+1]A[k+1] | S[k+1]−S[k]S[k+1]-S[k]S[k+1]−S[k] |
A[k+2]A[k+2]A[k+2] | S[k+2]−S[k]S[k+2]-S[k]S[k+2]−S[k] |
......... | ......... |
A[M]A[M]A[M] | S[M]−S[k]S[M]-S[k]S[M]−S[k] |
A[1]A[1]A[1] | S[1]+S[M]−S[k]S[1]+S[M]-S[k]S[1]+S[M]−S[k] |
......... | ......... |
A[k]A[k]A[k] | S[k]+S[M]−S[k]S[k]+S[M]-S[k]S[k]+S[M]−S[k] |
代码
#include<cstdio>
#include<algorithm>
#define K 100001
using namespace std;
int n,m,k;
long long row[K],col[K],w,c[K],ans,c1,t1;
int main()
{scanf("%d%d%d",&n,&m,&k);for (int i=1;i<=k;i++){scanf("%lld%lld",&c1,&t1);row[c1]++;col[t1]++;}if (k%n==0&&k%m==0) w=0,printf("both ");else if (k%n==0) w=1,printf("row ");else if (k%m==0) w=2,printf("column ");else {printf("impossible");return 0;}if (!w||w==1)//行需要处理{for (int i=1;i<=n;i++)c[i]=c[i-1]+row[i]-k/n;//前缀和sort(c+1,c+1+n);//排序for (int i=1;i<=n;i++)ans+=abs(c[i]-c[(n+1)/2]);//计算价值}if (!w||w==2)//列需要处理{for (int i=1;i<=m;i++)c[i]=c[i-1]+col[i]-k/m;sort(c+1,c+1+m);for (int i=1;i<=m;i++)ans+=abs(c[i]-c[(m+1)/2]);}printf("%lld",ans);
}