AcWing 七夕祭
Description
七夕节因牛郎织女的传说而被扣上了「情人节」的帽子。
于是TYVJ今年举办了一次线下七夕祭。
Vani同学今年成功邀请到了cl同学陪他来共度七夕,于是他们决定去TYVJ七夕祭游玩。
TYVJ七夕祭和11区的夏祭的形式很像。
矩形的祭典会场由N排M列共计N×M个摊点组成。
虽然摊点种类繁多,不过cl只对其中的一部分摊点感兴趣,比如章鱼烧、苹果糖、棉花糖、射的屋……什么的。
Vani预先联系了七夕祭的负责人zhq,希望能够通过恰当地布置会场,使得各行中cl感兴趣的摊点数一样多,并且各列中cl感兴趣的摊点数也一样多。
不过zhq告诉Vani,摊点已经随意布置完毕了,如果想满足cl的要求,唯一的调整方式就是交换两个相邻的摊点。
两个摊点相邻,当且仅当他们处在同一行或者同一列的相邻位置上。
由于zhq率领的TYVJ开发小组成功地扭曲了空间,每一行或每一列的第一个位置和最后一个位置也算作相邻。
现在Vani想知道他的两个要求最多能满足多少个。
在此前提下,至少需要交换多少次摊点。
Input
第一行包含三个整数N和M和T,T表示cl对多少个摊点感兴趣。
接下来T行,每行两个整数x, y,表示cl对处在第x行第y列的摊点感兴趣。
Output
首先输出一个字符串。
如果能满足Vani的全部两个要求,输出both;
如果通过调整只能使得各行中cl感兴趣的摊点数一样多,输出row;
如果只能使各列中cl感兴趣的摊点数一样多,输出column;
如果均不能满足,输出impossible。
如果输出的字符串不是impossible, 接下来输出最小交换次数,与字符串之间用一个空格隔开。
Data Size
- 1≤N,M≤100000,
0≤T≤min(N∗M,100000),
1≤x≤N,
1≤y≤M
Sample Input
2 3 4 1 3 2 1 2 2 2 3
Sample Output
row 1
题解:
- 数学题(中位数) + 贪心
- 嘛少见的锻炼思维的好题QAQ
- 首先发现交换两行的摊点不会影响到两列;交换两列的摊点不会影响到两行
- 所以行和列是独立的,故可以分别处理,问题就转换成了:
- 通过最少次数使每行中的兴趣摊点一样多
- 通过最少次数使每列中的兴趣摊点一样多
- 进一步思考,如果T % n != 0,说明不能使每行的兴趣摊点一样多;相同的,如果T % m != 0,说明不能使每列的兴趣摊点一样多。那么如果T % n == 0 或者 T % m == 0的情况怎么办呢?
- 接下来以行为例(列同理):
- 先考虑没有环的情况:
- 因为最终每行的兴趣摊点个数是T / n(平均数),所以一开始就让原数组减掉这个平均数,那么就要通过交换位置,使得数组上每一个位置的值为0即符合题意。
- 假如减掉平均数后c[1] = 3,代表着1这个位置的摊点比平均数多3。那么ans += 3, c[2] += c[1], c[1] = 0,这样写表示将1位置多出来的3个摊点挪到2位置去。假如c[2] = -1,那么加上c[1]后就变成了2,那么ans += 2, c[3] += c[2], c[2] = 0,以此类推。最终ans既为所求
- 可以推出,ans = \(\sum|C[i]|, (1<= i <= N);C[i] = \sum A[j], (1<= j <= i)\)
- A为初始数组减掉平均数后的数组,C为A的前缀和
- 上面的运算过程实际上是“均分纸牌”这题的思路。
- 你可能会问:不是每次只能交换两个摊点吗?怎么你一下把3个摊点都挪动了。
- 因为最终的时候1位置肯定要挪动3次,每次都是挪到2位置。那么计算就不用一次一次算,直接一次性算既可。
- 那么,有环怎么办呢?
- 一种暴力的想法就是断环成链,找最优的。但复杂度就要炸裂。
- 假设我们在第k个人之后,第k + 1个人之前断环,那么这N个位置的状态就是:
- A[k + 1] ----- C[k + 1] - C[k]
- A[k + 2] ----- C[k + 2] - C[k]
- ... ...
- A[N] ----- C[N] - C[k]
- A[1] ----- C[N] - C[k] + C[1]
- ... ...
- A[k] ----- C[N] - C[k] + C[k]
- 因为C[N]等于0(最终每个位置都为0)
- 所以按照上面推出来的ans公式,可以得到:
- \(ans = \sum|C[i] - C[k]|, (1 <= i <= N)\)
- 这个东西像什么?中位数!几何意义就是一堆点到一个定点的几何距离之和。要使距离之和最小,那么那个定点就是一堆点中的中位点啊!
- 所以先求出初始数组减掉平均数后的数组A,再通过A求出前缀和数组C,然后找到C中的中位数,既可得到ans。对于列的情况同理!
- 总结一下我们的思考过程:本题 -> 均分纸牌 -> 环形均分纸牌 -> 数学分析 -> 中位数 -> 求解
- 这种模型转换的思想十分的令人激动,需要反复反复来捣鼓这道题
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define N 100005
#define LL long long
using namespace std;
LL n, m, T, flag;
LL a[N], b[N], c[N];
LL do1()
{
LL ans = 0, k;
if(n % 2) k = (LL)(n / 2) + 1;
else k = n / 2;
for(LL i = 1; i <= n; i++) c[i] = c[i - 1] + (a[i] - T / n);
sort(c + 1, c + 1 + n); //别漏了排序
for(LL i = 1; i <= n; i++) ans += abs(c[i] - c[k]);
return ans;
}
LL do2()
{
LL ans = 0, k;
if(n % 2) k = (LL)(m / 2) + 1;
else k = m / 2;
for(LL i = 1; i <= m; i++) c[i] = c[i - 1] + (b[i] - T / m);
sort(c + 1, c + 1 + m); //别漏了排序
for(LL i = 1; i <= m; i++) ans += abs(c[i] - c[k]);
return ans;
}
int main()
{
cin >> n >> m >> T;
for(LL i = 1; i <= T; i++)
{
LL x, y;
scanf("%lld%lld", &x, &y);
a[x]++, b[y]++;
}
if(T % n == 0 && T % m == 0) cout << "both " << do1() + do2();
else if(T % n == 0) cout << "row " << do1();
else if(T % m == 0) cout << "column " << do2();
else cout << "impossible";
return 0;
}