AcWing 105 七夕祭

题目描述:

七夕节因牛郎织女的传说而被扣上了「情人节」的帽子。于是TYVJ今年举办了一次线下七夕祭。Vani同学今年成功邀请到了cl同学陪他来共度七夕,于是他们决定去TYVJ七夕祭游玩。TYVJ七夕祭和11区的夏祭的形式很像。

矩形的祭典会场由N排M列共计N×M个摊点组成。虽然摊点种类繁多,不过cl只对其中的一部分摊点感兴趣,比如章鱼烧、苹果糖、棉花糖、射的屋……什么的。Vani预先联系了七夕祭的负责人zhq,希望能够通过恰当地布置会场,使得各行中cl感兴趣的摊点数一样多,并且各列中cl感兴趣的摊点数也一样多。不过zhq告诉Vani,摊点已经随意布置完毕了,如果想满足cl的要求,唯一的调整方式就是交换两个相邻的摊点。两个摊点相邻,当且仅当他们处在同一行或者同一列的相邻位置上。由于zhq率领的TYVJ开发小组成功地扭曲了空间,每一行或每一列的第一个位置和最后一个位置也算作相邻。现在Vani想知道他的两个要求最多能满足多少个。在此前提下,至少需要交换多少次摊点。

输入格式

第一行包含三个整数N和M和T,T表示cl对多少个摊点感兴趣。接下来T行,每行两个整数x, y,表示cl对处在第x行第y列的摊点感兴趣。

输出格式

首先输出一个字符串。如果能满足Vani的全部两个要求,输出both;如果通过调整只能使得各行中cl感兴趣的摊点数一样多,输出row;如果只能使各列中cl感兴趣的摊点数一样多,输出column;如果均不能满足,输出impossible。如果输出的字符串不是impossible, 接下来输出最小交换次数,与字符串之间用一个空格隔开。

数据范围

1≤N,M≤100000,
0≤T≤min(N∗M,100000),
1≤x≤N,
1≤y≤M

输入样例:

2 3 4
1 3
2 1
2 2
2 3

输出样例:

row 1

分析:

第一个问题,什么情况下可以通过调整使各行感兴趣摊点一样多,什么情况下可以通过调整使各列感兴趣摊点一样多。在感兴趣摊点总数T能被行数n整除时,可满足第一个要求,能被列数m整除时,能满足第二个要求。

第二个问题,如何调整交换次数最少?可以发现,交换两相邻列的摊点不会影响各行感兴趣摊点的数量,同样,交换两行也不会影响各列感兴趣摊点数量。原问题就转化为了先左右交换使得各列感兴趣摊点数相同,再上下交换使得各行感兴趣摊点数相同两个子问题。

先引入一个均分纸牌问题。M个人排成一行,他们手里分别有C[1] ~ C[M]张纸牌,在每次操作中,每个人可以把自己手里的一张纸牌交给相邻的一个人,求至少需要多少步操作才能使得每个人手中纸牌数相等。

比如初始纸牌数为1 3 2 2,那么第二个人给第一个人一张纸牌即可,如果是0 1 5 2,还可以理解为第二个人要给第一个人2张牌,只要在此操作前,第二个人先从第三个人处拿走足够的牌即可,我们只关心操作数而不关心操作顺序。

整体来看,我们先使第一个人手中拿的牌是T/M(平均数),再使第二个人达到平均数,直至最后一个人问题就解决了。设G[i] = C[1] + … + C[i]即C的前缀和,那么每个前缀最初共持有G[i]张纸牌,最终会持有i * T / M张纸牌,会与后面的人发生二者之差绝对值次交换,第i个人会与后面一人发生的交换次数也就是i * T / M – G[i]的绝对值,将所有人的交换次数累加起来也就是所求的总操作数。

设A[i] = C[i] – T / M,即一开始就让每个人手中持有的纸牌数减去平均数,并且让最终每个人手中都没有纸牌,那么答案显然是不变的。设S[i] = A[1] + … + A[i] = G[i] – i * T / M,那么问题的解就转化为了S[i]的绝对值(i从1到M)求和。

可以发现,本题相当于环形纸牌问题,如果按照上面的前缀和思路会发现交换得没完没了。分析可得,环形纸牌问题中,一定存在某个相邻的两个人没有发生交换(最优情况下)。比如1 2 3 4 5五个人围成一圈,如果是正常的纸牌问题,1给2纸牌,2再给3,3给4,4给5,相当于纸牌从1传递到了5,但是环形的如果5还传给1,这么多步就白操作了,必然不是最优解。如果所有相邻的两个人都交换了纸牌,那必然会产生a给了b纸牌,b又给了a纸牌的不利情况。既然知道了最优情况下一定有两个人没发生交换,不妨枚举环形纸牌断开的位置,找到其中的最小解即可。

初始环为A[1] A[2] … A[M],当然最后一个与第一个相连,初始前缀和为S[1] S[2] … S[M],在第k个人后面将环断开,则前缀和发生了变化,S[k+1]原本就是S[k+1],断开后k之前的都没有了,前缀和变为S[k+1] – S[k],同理S[M] = S[M] – S[k],S[1] = S[M] – S[k] + S[1],S[k] = S[k] + S[M] – S[k]。注意,我们这里的A[i]是C[i] – T / M,也就是每个人原始纸牌数减去平均数的值,所以初始情况下S[M] = 0,因为最终每个人手中纸牌数都是0了。于是上面的前缀和就变成了S[i] = S[i] – S[k],根据前面推导的公式,问题的解是S[i]的绝对值(i从1到M)求和,我们需要找到一个合适的位置k,使得解最小,回忆前面的货仓选址问题AcWing 104 货仓选址,S[i]就等价于商店位置,S[k]就等价于货仓位置,所以将S[i]排序下,其中位数即是我们要找的k。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 100006;
int n,m,t,x[N],y[N],a[N],s[N];
int main(){
	cin>>n>>m>>t;
	for(int i = 1;i <= t;i++)	cin>>x[i]>>y[i];
	bool row = !(t % n),column = !(t % m);
	if(row){
		if(column)	cout<<"both ";
		else	cout<<"row ";
	}
	else{
		if(column)	cout<<"column ";
		else{
			cout<<"impossible"<<endl;
			return 0;
		}
	}
	ll ans = 0;
	if(row){
		int num = t / n;
		memset(a,0,sizeof(a));
		for(int i = 1;i <= t;i++)	a[x[i]]++;
		for(int i = 1;i <= n;i++)	a[i] -= num;
		s[0] = 0;
		for(int i = 1;i <= n;i++)	s[i] = s[i-1] + a[i];
		sort(s + 1,s + n + 1);
		for(int i = 1;i <= n / 2;i++)	ans += s[n-i+1] - s[i];
	}
	if(column){
		int num = t / m;
		memset(a,0,sizeof(a));
		for(int i = 1;i <= t;i++)	a[y[i]]++;
		for(int i = 1;i <= m;i++)	a[i] -= num;
		s[0] = 0;
		for(int i = 1;i <= m;i++)	s[i] = s[i-1] + a[i];
		sort(s + 1,s + m + 1);
		for(int i = 1;i <= m / 2;i++)	ans += s[m-i+1] - s[i];
	}
	cout<<ans<<endl;
	return 0;
}

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值