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;
}