题意
给定一个 n ∗ m n*m n∗m的矩阵,其中第一行和最后一行相邻,第一列和最后一列相邻。已知 t t t个标记点坐标,每个标记点可以上下左右移动。有两个条件:
- 每行标记点数相同
- 每列标记点数相同
问最多能满足多少个条件,并求出最少移动步数。
思路
上下移动只会影响条件一,左右移动只会影响条件二,因此可以把行列分开处理,下面只考虑行,该题转化为环形“均分纸牌”,可通过枚举分割点将环形“均分纸牌”转化为线性“均分纸牌”。下面考虑分割位置为
1
1
1和
n
n
n之间的情况:
设
c
i
c_i
ci为第i位的纸牌数,答案为
a
n
s
ans
ans;
经推导易得为
a
n
s
=
∑
i
=
1
n
∣
i
∗
p
−
∑
j
=
1
i
c
j
∣
=
∑
i
=
1
n
∣
∑
j
=
1
i
p
−
∑
j
=
1
i
c
j
∣
=
∑
i
=
1
n
∣
∑
j
=
1
i
(
p
−
c
j
)
∣
ans=\sum\limits_{i=1}^n|i*p-\sum\limits_{j=1}^ic_j|=\sum\limits_{i=1}^n|\sum\limits_{j=1}^ip-\sum\limits_{j=1}^ic_j|=\sum\limits_{i=1}^n|\sum\limits_{j=1}^i(p-c_j)|
ans=i=1∑n∣i∗p−j=1∑icj∣=i=1∑n∣j=1∑ip−j=1∑icj∣=i=1∑n∣j=1∑i(p−cj)∣,其中
p
=
∑
i
=
1
n
c
i
n
p=\frac{\sum\limits_{i=1}^nc_i}{n}
p=ni=1∑nci(
c
i
c_i
ci的平均数);
设
s
i
=
∑
j
=
1
i
(
p
−
c
j
)
s_i=\sum\limits_{j=1}^i(p-c_j)
si=j=1∑i(p−cj)
特别地
s
n
=
∑
i
=
1
n
p
−
∑
i
=
1
n
c
i
=
n
∗
∑
i
=
1
n
c
i
n
−
∑
i
=
1
n
c
i
=
0
s_n=\sum\limits_{i=1}^np-\sum\limits_{i=1}^nc_i=n*\frac{\sum\limits_{i=1}^nc_i}{n}-\sum\limits_{i=1}^nc_i=0
sn=i=1∑np−i=1∑nci=n∗ni=1∑nci−i=1∑nci=0
则
a
n
s
=
∑
i
=
1
n
∣
s
i
∣
ans=\sum\limits_{i=1}^n|s_i|
ans=i=1∑n∣si∣
若分割位置改为
k
k
k和
k
+
1
k+1
k+1之间,则
s
i
′
=
{
s
i
+
s
n
−
s
k
1
≤
i
≤
k
s
i
−
s
k
k
<
i
≤
n
s'_i=\begin{cases} s_i+s_n-s_k & 1\leq i\leq k \\ s_i-s_k & k<i\leq n \end{cases}
si′={si+sn−sksi−sk1≤i≤kk<i≤n
又因为
s
n
=
0
s_n=0
sn=0
所以
s
i
′
=
s
i
−
s
k
s'_i=s_i-s_k
si′=si−sk
a
n
s
′
=
∑
i
=
1
n
∣
s
i
′
∣
=
∑
i
=
1
n
∣
s
i
−
s
k
∣
ans'=\sum\limits_{i=1}^n|s'_i|=\sum\limits_{i=1}^n|s_i-s_k|
ans′=i=1∑n∣si′∣=i=1∑n∣si−sk∣
问题成功转化为“货仓选址”问题,当
s
k
s_k
sk为
{
s
i
}
\{s_i\}
{si}中位数时
a
n
s
ans
ans有最小值。
代码
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <cmath>
#include <iostream>
using namespace std;
#define fo(i, x, y) for (int i = x; i <= y; ++i)
#define fd(i, x, y) for (int i = x; i >= y; --i)
typedef long long ll;
const int maxn = 1e5 + 5;
struct point{int x, y;}p[maxn];
int n, m, t;
ll a[maxn], s[maxn];
int getint()
{
char ch;
int res = 0, p;
while (!isdigit(ch = getchar()) && (ch ^ '-'));
p = ch == '-'? ch = getchar(), -1 : 1;
while (isdigit(ch))
res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return res * p;
}
ll getans(ll *a, int n)
{
ll sum = 0;
fo(i, 1, n) sum += a[i];
if (sum % n) return -1;
fo(i, 1, n) a[i] -= sum / n;
fo(i, 1, n) s[i] = s[i - 1] + a[i];
sort(s + 1, s + 1 + n);
ll ans = 0;
fo(i, 1, n) ans += abs(s[i] - s[(n + 1) / 2]);
return ans;
}
int main()
{
n = getint(); m = getint(); t = getint();
fo(i, 1, t)
{
p[i].x = getint(); p[i].y = getint();
}
fo(i, 1, t) a[p[i].x]++;
ll ansx = getans(a, n);
memset(a, 0, sizeof(a));
fo(i, 1, t) a[p[i].y]++;
ll ansy = getans(a, m);
if ((~ansx) && (~ansy)) printf("both %lld\n", ansx + ansy);
else if (~ansx) printf("row %lld\n", ansx);
else if (~ansy) printf("column %lld\n", ansy);
else printf("impossible\n");
return 0;
}