AcWing 105 七夕祭

题解:
这个题目是一个综合性很强的题目,可以类比为环形分纸牌的问题。我们注意到对于每一行上的相邻元素,交换次序后只会影响到每一列的结果,同样的交换每一列上的相邻元素影响的也只是每一行的结果。
所以我们可以把行和列分隔开分别讨论。但是不管对于行还是对于列,我们都需要满足 K % N(M) = 0不然肯定无法均分到每一行每一列上。
我们可以先在原数组中处理一下,对于每一行每一列的摊点数目,我们都先预处理为A[i] = A[i] - k/m(n),这样就相当于要把所有的A[i]都变成零,我们知道对于一般均分纸牌的问题,答案是sum(s[i]),s[i]为A[i]前缀和,但是这个题目是一个环形。我们仔细想一下不难发现,对于每一行/列, 总会存在一个位置k没有发生交换操作,(可以反证下,如果每行/列都进行了交换,那就会出现有一行/列的摊位数不为0),所以我们就只需要枚举以下k的位置就可以了。
我们假设这个环在k处断开,那么前缀和数组的变化就是每个值减去s[k].
最后得到的结果就是:
a n s = s u m ( ∣ S [ i ] − S [ k ] ∣ ) ans = sum(|S[i] - S[k]|) ans=sum(S[i]S[k])
那么问题就变成了,当k取何值是ans最小,不难发现这个就是中位数的问题了,也就是(n + 1) >> 1
AC代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int maxn = 1e5 + 19;
int r[maxn] = {0},s[maxn] = {0},c[maxn] = {0};
int n,m,k;
ll rr = 0,cc = 0,ans1 = 0,ans2 = 0;
bool cmd(int x,int y)
{
    if(x != y) return x < y;
}
bool calc_row()//走行数
{
    memset(s,0,sizeof(s));
    if(rr % n) return 0;
    for(int i = 1;i <= n;i++) {
        r[i] -= rr/n;
        s[i] = s[i - 1] + r[i];
    }
    sort(s + 1,s + 1 + n,cmd);
    int k = (n + 1) >> 1;
    for(int i = 1;i <= n;i++){
        ans1 += abs(s[i] - s[k]);
    }
    return 1;
}
bool calc_cl()//走列数
{
    memset(s,0,sizeof(s));
    if(cc % m) return 0;
    for(int i = 1;i <= m;i++){
        c[i] -= cc/m;
        s[i] = s[i - 1] + c[i];
    }
    sort(s + 1,s + 1 + m,cmd);
    int k = (m + 1) >> 1;
    for(int i = 1;i <= m;i++){
        ans2 += abs(s[i] - s[k]);
    }
    return 1;
}
int main()
{
    cin >> n >> m >> k;
    for(int i = 1;i <= k;i++){
        int x,y;
        cin >> x >> y;
        r[x]++;
        c[y]++;
        rr++;
        cc++;
    }
    bool cnt1 = calc_row(),cnt2 = calc_cl();
    if(cnt1 && cnt2){
        cout << "both " << ans1 + ans2 << endl;
    }
    else if(cnt1){
        cout << "row " << ans1 << endl;
    }
    else if(cnt2){
        cout << "column " << ans2 << endl;
    }
    else{
        cout << "impossible" << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CUCKyrie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值