Loading

acwing题解——七夕祭

1题目大意

https://www.acwing.com/problem/content/107/

2解析

2.1 第一眼

其实这道题第一眼你会发现是行和列分别来讨论,所以我们最后要的信息其实是每一行,每一列的感兴趣的货摊个数是多少。

2.2简化问题

很多人到这里就开始没有思路——包括我,这个时候我们简化问题,尝试向我们学过的内容去靠拢。

如果,这个题不使环形,那么,其实这个题就变成了一个均分纸牌问题,也就变得容易解决

那么环形怎么处理?

容易知道,均分纸牌不可能把纸牌穿过一周,因为这样就相当于没传,肯定不是最优解。所以最优解一定是从某个地方分叉的均分纸牌。

2.3优化算法

到这里其实可以打一个朴素算法出来,枚举断点,让后均分纸牌,我们来思考一下,有没有更好地做法。首先把问题标准化。

如果我们给每一个数都减去一个他们的平均数,那么其实就相当于一个最终让所有数等于0的一个均分纸牌。

最终答案一定与这几个数有关系。不妨来找一下他们的关联。

设一共有n个数,分别为\(a_1,a_2,a_3,...a_n\),那么答案是多少呢?

第一摞牌需要移动\(a_1\)次给第二摞牌,那么第二摞牌就要有\(a_1+a_2\)次,以此类推,答案应当为:

\(a_1+(a_1+a_2)+(a_1+a_2+a_3)+...+(a_1+a_2+a_3+....a_n)\)

这就是前n个数的前缀和之和。

不妨设\(s_i\)为以i结尾的a的前缀和,则答案为:

\(s_1+s_2+...s_n\)

我们继续观察,上述朴素算法的时间浪费在枚举断点里,思考,如果在k后面断开,即把1到k这k摞牌放到第n摞后面,这几摞的前缀和会怎么变化,易得如下变化(新数组有S表示):

\(S_1=s_n+s_1-s_k,S_2=s_n+s_2-s_k...S_k=s_n+s_k-s_k,S_{k+1}=s_{k+1}-s_k,...S_n=S_n-s_k\)

因为我们事先将问题标准化了,所以\(s_n=0\)所以一切都变成了\(s_i-s_k\)的绝对值。

所以我们想要得到的就是\(\sum_{i=1}^n|s_i-s_k|,k=[1,n]\)的最大值。

观察一下这个式子,这不就是货仓选址问题吗?

于是sort一遍取中位数即可。

2.4补充

判否的话判断能否整除即可,均分纸牌有解的充要条件时t能被n整除。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ld long double
#define ll long long
#define ull unsigned long long
#define N 100100
#define M number
using namespace std;

ll n,m,t;
ll c[N],r[N],ans,s[N];

int main(){
	scanf("%lld%lld%lld",&n,&m,&t);
	for(ll i=1;i<=t;i++){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		r[x]++;c[y]++;
	}
	bool opc=true,opr=true;
	if(t%n!=0) opr=0;if(t%m!=0) opc=0;
	if(!opr&&!opc){
		printf("impossible");
		return 0;
	}
	if(opr){
		for(ll i=1;i<=n;i++){
			r[i]-=t/n;
			s[i]=s[i-1]+r[i];
		}
		sort(s+1,s+n+1);
		ll zws=s[1+n>>1];
		for(ll i=1;i<=n;i++) ans+=abs(s[i]-zws); 
	}
	if(opc){
		for(ll i=1;i<=m;i++){
			c[i]-=t/m;
			s[i]=s[i-1]+c[i];
		}
		sort(s+1,s+m+1);
		ll zws=s[1+m>>1];
		for(ll i=1;i<=m;i++) ans+=abs(s[i]-zws);
	}
	if(opr&&!opc) printf("row ");
	else if(!opr&&opc) printf("column ");
	else printf("both ");
	printf("%lld",ans);
	return 0;
}
posted @ 2021-03-01 20:50  hyl天梦  阅读(108)  评论(0编辑  收藏  举报