蓝书(算法竞赛进阶指南)刷题记录——CH0502 七夕祭

版权声明:转载请注明原出处啦(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/82051037

题目:0502 七夕祭.

题目大意:给出一个N*M的矩阵,以及一个T个选定点,现在这所有点都可以与相邻的点交换,且边界也可以与另一条边界交换,现在问能否是每行和每列的选定点的数目相同,以及最少要多少步.

很显然地,可以发现行列之间互相不影响,所以我们先把两个问题拆开来,接下来我们只讨论列,行同理.

然后我们发现这个问题貌似跟均分纸牌有点联系,建议先去做一下这道题.

我们从均分纸牌这道题可以推出,其实无解的情况就是列数无法整除总点数.

我们现在记C[i]为第i列的点数,现在我们按照均分纸牌的方式推,我们假设c[1]~c[m]的平均数为x,那么我们得到的最少移动步数其实就是\sum_{i=1}^{m}|i*x-C[i]|,其中C[i]是c[i]的前缀和.

现在我们设a[i]=c[i]-x,就是一开始就减去中位数,那么我们在设A[i]是a[i]的前缀和,那么公式就可变为\sum_{i=1}^{m}|A[i]|.

现在我们继续使用均分纸牌这个思想,我们发现其实头尾相邻这个问题其实就是转化为一个环状均分纸牌问题.

那么我们看一下在第k个地方开始计算公式会变成什么,会变成一个\sum_{i=k}^{m}|A[k]-A[k]|+\sum_{i=1}^{k-1}|A[i]+A[m]-A[K]|.

现在我们发现,若有解,则A[m]一定为0,所以我们可以在把式子转化成\sum_{i=1}^{m}|A[i]-A[k]|.

我们现在发现这个式子跟货仓选址很像,所以我们也取中位数来做.

扫描二维码关注公众号,回复: 3454848 查看本文章

代码如下:

#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;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/82051037