题目:CH0502/BZOJ3032.
题目大意:给出一个
n
∗
m
n*m
n∗m的矩阵,以及一个
t
t
t个选定点,现在这所有点都可以与相邻的点交换,且边界也可以与另一条边界交换,现在问能否是每行和每列的选定点的数目相同,以及最少要多少步.
1
≤
n
,
m
≤
1
0
5
,
1
≤
t
≤
m
i
n
(
n
∗
m
,
1
0
5
)
1\leq n,m\leq 10^5,1\leq t\leq min(n*m,10^5)
1≤n,m≤105,1≤t≤min(n∗m,105).
很显然地,可以发现行列之间互相不影响,所以我们先把两个问题拆开来,接下来我们只讨论列,行同理.
然后我们发现这个问题貌似跟均分纸牌有点联系,建议先去做一下.
我们从均分纸牌这道题可以推出,其实无解的情况就是列数无法整除总点数.
现在记 c [ i ] c[i] c[i]为第 i i i列的点数,现在我们按照均分纸牌的方式推,我们假设 c [ 1 ] c [ m ] c[1]~c[m] c[1] c[m]的平均数为 x x x,设 C [ i ] C[i] C[i]为 c [ i ] c[i] c[i]的前缀和,那么最少移动步数为 ∑ i = 1 m ∣ i ∗ x − C [ i ] ∣ \sum_{i=1}^{m}|i*x-C[i]| ∑i=1m∣i∗x−C[i]∣.
设 a [ i ] = c [ i ] − x a[i]=c[i]-x a[i]=c[i]−x, A [ i ] A[i] A[i]为 a [ i ] a[i] a[i]的前缀和,那么原式等价于 ∑ i = 1 m A [ i ] \sum_{i=1}^{m}A[i] ∑i=1mA[i].
现在我们继续使用均分纸牌这个思想,我们发现其实头尾相邻这个问题其实就是转化为一个环状均分纸牌问题.
考虑在第 k k k个点开始计算,答案会变成 ∑ i = k m ∣ A [ k ] − A [ k ] ∣ + ∑ i = 1 k − 1 ∣ A [ i ] + A [ m ] − A [ k ] ∣ \sum_{i=k}^{m}|A[k]-A[k]|+\sum_{i=1}^{k-1}|A[i]+A[m]-A[k]| ∑i=km∣A[k]−A[k]∣+∑i=1k−1∣A[i]+A[m]−A[k]∣.
我们发现,问题若有解则一定有 A [ m ] = 0 A[m]=0 A[m]=0,所以把式子转化成 ∑ i = 1 m ∣ A [ i ] − A [ k ] ∣ \sum_{i=1}^{m}|A[i]-A[k]| ∑i=1m∣A[i]−A[k]∣,发现这个式子跟货仓选址很像,所以我们也取中位数来做.
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000;
int n,m,t;
LL sumx,sumy,sx[N+9],sy[N+9],ansx,ansy,x[N+9],y[N+9];
bool flag0=0,flag1=0;;
Abigail into(){
scanf("%d%d%d",&n,&m,&t);
int u,v;
for (int i=1;i<=t;i++){
scanf("%d%d",&u,&v);
x[u]++;y[v]++;
}
}
Abigail work(){
for (int i=1;i<=n;i++)
sx[i]=sx[i-1]+x[i];
for (int i=1;i<=m;i++)
sy[i]=sy[i-1]+y[i];
if (t%n==0) flag0=1;
if (t%m==0) flag1=1;
sumx=t/n,sumy=t/m;
for (int i=1;i<=n;i++)
sx[i]-=sumx*i;
for (int i=1;i<=m;i++)
sy[i]-=sumy*i;
sort(sx+1,sx+1+n);
sort(sy+1,sy+1+m);
for (int i=1;i<=n;i++)
ansx+=abs(sx[i]-sx[1+n>>1]);
for (int i=1;i<=m;i++)
ansy+=abs(sy[i]-sy[1+m>>1]);
}
Abigail outo(){
if (flag0){
if (flag1) printf("both %lld\n",ansx+ansy);
else printf("row %lld\n",ansx);
}else{
if (flag1) printf("column %lld\n",ansy);
else printf("impossible\n");
}
}
int main(){
int T=1;
while (T--){
into();
work();
outo();
}
return 0;
}