洛谷P10453 七夕祭题解
七夕祭 P10453 (前缀和)
P10453 七夕祭 - 洛谷
矩形的祭典会场由 N 排 M 列共计 \(N \times M\) 个摊点组成。
虽然摊点种类繁多,不过 cl 只对其中的一部分摊点感兴趣,比如章鱼烧、苹果糖、棉花糖、射的屋…… 么的。
Vani 预先联系了七夕祭的负责人 zhq, 希望能够通过恰当地布置会场,使得各行中 cl 感兴趣的摊点数一样多,并且各列中 cl 感兴趣的摊点数也一样多。
不过 zhq 告诉 Vani, 摊点已经随意布置完毕了,如果想满足 cl 的要求,唯一的调整方式就是交换两个相邻的摊点。
两个摊点相邻,当且仅当他们处在同一行或者同一列的相邻位置上。
由于 zhq 率领的 TYVJ 开发小组成功地扭曲了空间,每一行或每一列的第一个位置和最后一个位置也算作相邻。
现在 Vani 想知道他的两个要求最多能满足多少个。
在此前提下,至少需要交换多少次摊点。
输入格式
第一行包含三个整数 N 和 M 和 T , T 表示 cl 对多少个摊点感兴趣。
接下来 T 行,每行两个整数 x , y , 表示 cl 对处在第 行第 y 列的摊点感兴趣。
输出格式
首先输出一个字符串。
如果能满足 Vani 的全部两个要求,输出 both;
如果通过调整只能使得各行中 cl 感兴趣的摊点数一样多,输出 row;
如果只能使各列中 cl 感兴趣的摊点数一样多,输出 colum;
如果均不能满足,输出 impossible。
如果输出的字符串不是 impossible, 接下来输出最小交换次数,与字符串之间用一个空格隔开。
正式题解
1. 找最浅显性质
-
行与列的变换都是独立事件 (互不影响)。
-
容斥思想:当 \(t \% n == 0\) 时,才可能满足行的要求,列同理。
2. 推交换次数表达式
既然已经判断有无解,问题就可以转化为:
有 n 个人和 m 张牌,坐成一个环,第 i 个人有 x [i] 张牌,相邻的人交换牌,使得每个人手中的牌数量相等。
还是先找最浅显性质。
所以我们可以看看在哪破环最优,列出对应的表达式。
- 观察最特殊点 -- 环的处理 (环的处理方法): 发现一定有两个人之间没有换牌 (反证法证明:如果每两个人间都交换,那么牌一定被换一圈,不是最优)。
具体推导
-
每个人手中最后 \(\frac{m}{n}\) 张,设每个人多或少 \(a[i] = \frac{m}{n} - x[i]\)。
-
环上不好看,先看链上:
-
对于第 1 个人,还差 \(a[i] = \frac{m}{n} - x[i]\), 只能找 2 要。
-
2 此时还差 \(a[2] + a[1]\), 只能找 3 要。
-
发现有前缀和关系:
-
a 的前缀和数组是 s:
\(ans = \sum s[i]\)
-
- 扩展到环上:假设 k 和 k-1 不换牌,a 的前缀和数组是 S :
-
\([k, n]\) 的人:
\[\sum_{j=k}^{n}|s[j] - s[k-1]| \] -
\([1, k-1]\) 的人:
\[\sum_{j=1}^{k-1}|s[j] + s[n] - s[k-1]| \] -
发现两个式子差 \(s[n]\), 仔细观察 \(s[n]\) 的性质,为所有人对于全部牌的多或少的量,不难发现只能是 1 :. 对于 \([1, n]\)
就是求它的最小值,把绝对值视作距离:问题转化为 “货舱选址问题”
数轴上有一些仓库,位置是 s [i], 找一个仓库,使得它到所有仓库距离和最小。
- 结论:k-1 就是 s 的中位数。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100009;
int n, m;
int row[N], col[N];
int t;
int a[N];
int cal(int *x, int n)
{
memset(a, 0, sizeof a);
for (int i = 1; i <= n; ++i)
a[i] = t / n - x[i];
for (int i = 1; i <= n; ++i)
a[i] += a[i - 1];
int mid = (n + 1 >> 1);
sort(a + 1, a + 1 + n);
int d = a[mid];
int res = 0;
for (int i = 1; i <= n; ++i)
res += (abs(a[i] - a[mid]));
return res;
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
cin >> n >> m >> t;
memset(row, 0, sizeof row);
memset(col, 0, sizeof col);
for (int i = 1; i <= t; ++i)
{
int x, y;
cin >> x >> y;
++row[x];
++col[y];
}
int ans = 0;
bool yr = 0, yc = 0;
if (!(t % n))
yr = 1;
if (!(t % m))
yc = 1;
if (yr && yc)
{
int p = cal(row, n) + cal(col, m);
cout << "both" << " " << p << "\n";
}
else if (yr)
{
cout << "row" << " " << cal(row, n) << "\n";
}
else if (yc)
{
cout << "column" << " " << abs(cal(col, m)) << "\n";
}
else
cout << "impossible" << "\n";
return 0;
}
部分题解思路来自蓝书

浙公网安备 33010602011771号