七夕祭 (前缀和,排序)

七夕祭 (前缀和,排序)

题意是中文的就不重复了。。

思路:

列之间的交换并不会影响行的结果,相似的,行之间的交换并不会影响列的结果

首先我们进行可行性判断

即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) inS[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序列的中位数能使前述式子值为最小 inS[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值