七夕祭 (前缀和,排序)
题意是中文的就不重复了。。
思路:
列之间的交换并不会影响行的结果,相似的,行之间的交换并不会影响列的结果
首先我们进行可行性判断
即t个点可以被n行或者m列所均分
可以抽象为将t个点均分给n或m个人(类似题目[均分纸牌][https://ac.nowcoder.com/acm/problem/16739])
以下以分给n个人为例
第i个人需要的纸牌数目为 A [ i ] = r o w [ i ] − ( t / n ) A[i]=row[i]-(t/n) A[i]=row[i]−(t/n),为正则为需要纸牌,从i+1个人手里拿A[i]个,i+1个人手里不够也能拿,先欠着
我们的目的就是最终让A数组全为0
易证得,A[i]的前缀和S[i],就是遵循上述操作之后,第i个人需要的纸牌数目,我们每次会对其进行|S[i]|次操作
所以答案等于 ∑ i n ∣ S [ i ] ∣ 其 中 S [ i ] = ∑ j i A [ j ] , A [ j ] = r o w [ j ] − ( t / n ) \sum_i^n|S[i]| 其中S[i]=\sum_j^iA[j],A[j]=row[j]-(t/n) ∑in∣S[i]∣其中S[i]=∑jiA[j],A[j]=row[j]−(t/n)
上述的线性的答案,但是本题目是环形的
容易想到,每次对n-1个人进行操作之后,最后剩下的那个人肯定不用进行操作
我们选取一个人k,从他断开环,转换为线性问题
此时序列为 A [ k + 1 ] , A [ k + 2 ] . . . A [ n ] , A [ 1 ] , A [ 2 ] . . . A [ k ] A[k+1],A[k+2]...A[n],A[1],A[2]...A[k] A[k+1],A[k+2]...A[n],A[1],A[2]...A[k]
其前缀和为 S [ k + 1 ] − S [ k ] , S [ k + 2 ] − S [ k ] , . . . S [ n ] − S [ k ] , S [ 1 ] + S [ n ] − S [ k ] , S [ 2 ] + S [ n ] − S [ k ] , S [ k ] + S [ n ] − S [ k ] S[k+1]-S[k],S[k+2]-S[k],...S[n]-S[k],S[1]+S[n]-S[k],S[2]+S[n]-S[k],S[k]+S[n]-S[k] S[k+1]−S[k],S[k+2]−S[k],...S[n]−S[k],S[1]+S[n]−S[k],S[2]+S[n]−S[k],S[k]+S[n]−S[k]
所以上述公式我们可以变形为
∑ i n ∣ S [ i ] − S [ k ] ∣ , 同 时 , 易 证 得 我 们 选 取 S 序 列 的 中 位 数 能 使 前 述 式 子 值 为 最 小 \sum_i^n|S[i]-S[k]|,同时,易证得我们选取S序列的中位数能使前述式子值为最小 ∑in∣S[i]−S[k]∣,同时,易证得我们选取S序列的中位数能使前述式子值为最小
所以S[k]取S的中位数即为答案
代码:
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long int;
int row[100005], col[100005], n, m, t;
int cnt = 0;
int main(void) {
scanf("%d%d%d", &n, &m, &t);
int x, y;
while (t--) {
scanf("%d%d", &x, &y);
row[x]++;
cnt++;
col[y]++;
}
bool rflag = cnt % n;
bool cflag = cnt % m;
int c1 = cnt / n;
int c2 = cnt / m;
long long ansc = 0;
long long ansr = 0;
if (!rflag) {//计算行操作数
for (int i = 0; i < n; i++) {
row[i] -= c1;
}
for (int i = 1; i < n; i++) {
row[i] += row[i - 1];
}
int base = 0;
sort(row, row + n);
if (n & 1) {
base = row[(n + 1) / 2-1];
}
else {
base = row[n / 2-1];
}
for (int i = 0; i < n; i++) {
ansr += labs(row[i] - base);
}
}
if (!cflag) {//计算列操作数
for (int i = 0; i < m; i++) {
col[i] -= c2;
}
for (int i = 1; i < m; i++) {
col[i] += col[i - 1];
}
int base = 0;
sort(col, col + m);
if (m & 1) {
base = col[(m + 1) / 2-1];
}
else {
base = col[m / 2-1];
}
for (int i = 0; i < m; i++) {
ansc += labs(col[i] - base);
}
}
if (!rflag && !cflag) {
printf("both %lld", ansc + ansr);
}
else if (!rflag) {
printf("row %lld", ansr);
}
else if (!cflag) {
printf("column %lld", ansc);
}
else {
printf("impossible");
}
return 0;
}