AcWing 105. 七夕祭
大致题意
有一个\(n×m\)的矩阵,矩阵上有\(t\)个点有摊位,两个摊位相邻,当且仅当它们在同一行位置上,且每一行和每一列的第一个位置和最后一个位置也算作相邻
求至少需要交换多少个相邻摊位,能使得各行各列上的摊位数均相等
\(1≤n,m≤100000\)
分析
经过观察可以发现交换上下相邻两个的节点只会改变某两行中的摊位数,交换左右两个相邻的节点只会改变某两列中的摊位数,因此可以把原问题拆成两个问题:
1.至少移动多少个相邻摊位可以使各行中的摊位数相同
2.至少移动多少个想来摊位可以使各列中的摊位数相同
先来考虑不是环形的情况
设\(a[i]\)表示第\(i\)行中的摊位数量
显然,这是个经典的均分纸牌问题,最终答案即为\(\sum_{i=1}^n|a[i]-\dfrac{t}{m}|\),列也同理
对于加上环形的情况,一种比较好想的处理方法是断换成链,枚举断开哪个点,但对于\(1≤n,m≤100000\)的数据范围肯定会T飞,考虑优化;
设\(sum[i]\)表示\(\sum_{i=1}^n|a[i]-\dfrac{t}{m}|\),即\(|a[i]-\dfrac{t}{m}|\)的前缀和
假设在第\(k\)个人后把环断开
显然,对于区间\([k+1,n]\)的行,其前缀和为\(sum[i]-sum[k]\)
对于区间\([1,k]\)之间的行,其前缀和为\(sum[i]+sum[n]-sum[k]\)
又因为\(sum[m]\)必定是\(0\),因此前缀和的变化就是每个位置都减掉\(sum[k]\)
答案即为\(\sum_{i=1}^n|sum[i]-sum[k]|\)
把每个\(sum[i]\)都看作数轴上的一个点,那么就变成了一个经典问题:
在一个数轴上有\(n\)个点,从中选出一个点,使得其他的点到该点的距离之和最小
将\(sum\)排序后取中位数即可
总复杂度\(O(nlogn+mlogm)\)
\(code\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 100010;
int n,m,t;
int row[MAXN];
int col[MAXN];
int cnt;
int ROW(){
int sum[MAXN];
int ans = 0;
int unneed = t/n;
for(int i=1;i<=n;i++) row[i]-=t/n;
for(int i=1;i<=n;i++) sum[i] = sum[i-1]+row[i];
sort(sum+1,sum+n+1);
for(int i=1;i<=n/2;i++) ans+=sum[n-i+1]-sum[i];
return ans;
}
int COL(){
int sum[MAXN];
int ans = 0;
int unneed = t/m;
for(int i=1;i<=m;i++) col[i]-=t/m;
for(int i=1;i<=m;i++) sum[i] = sum[i-1]+col[i];
sort(sum+1,sum+m+1);
for(int i=1;i<=m/2;i++) ans+=sum[m-i+1]-sum[i];
return ans;
}
signed main(){
cin>>n>>m>>t;
for(int i=1;i<=t;i++){
int x,y;
cin>>x>>y;
row[x]++;
col[y]++;
cnt++;
}
if(cnt%n==0&&cnt%m==0){
cout<<"both ";
cout<<ROW()+COL();
}
else if(cnt%n==0){
cout<<"row ";
cout<<ROW();
}
else if(cnt%m==0){
cout<<"column ";
cout<<COL();
}
else cout<<"impossible";
}

浙公网安备 33010602011771号