2020百度之星程序设计大赛一 Matrix
题目:
有一个二维平面,给定 \(length[1],length[2],length[3],length[4]\),画出 $4 $个正方形区域。
第 $ i $ 个区域为 \((x,y) | |x|<=length[i],|y|<=length[i]\)。
对于一个整点 \((x,y)\) 其权值为 \((|x|+|y|)\times cnt\),其中 \(cnt\) 为覆盖该点的区域个数。(点在区域边界上或者在区域内,都称为被区域覆盖)。
现在需要在至少被一个区域覆盖的整点点集中,选出 \(k\) 个两两不同的点,使得总权值和最小。
输出最小的权值总和。
\(1≤length[1]<length[2]<length[3]<length[4]≤10^9,0≤k≤10^{10}\)
分析:
暴力枚举每个点显然不可能了,我们可以考虑换个策略,因为要求最小的权值和,我们可以从权值入手枚举(从小到大)。
对于一个权值 \(w\) ,必然是 \(1,2,3,4\) 之中一个数的倍数(题目说了要乘以覆盖面数),假设这个权值是 \(3\) 的倍数,也就是能被三个面覆盖了,那么我们将权值除 \(3\) 得出当前点的 \(|x|+|y|=\frac{w}{3}\) 。
而对于函数 \(|x|+|y|=z\) 的图像我们清楚:
在这个图像上坐标点为整数的点的个数为 \(4\times z\) 个。
那如果我们现在知道了\(|x|+|y|=\frac{w}{3}\) ,我们要做的就是找出上面图像中位于第三层内和第四层外的点的个数。
比如图中绿色框内是有三层覆盖,蓝色框内是四层覆盖,而红色则是我们的目标函数,我们要找的就是位于绿框内且位于蓝框外的在红色函数上的整数点的个数,那其实就是用位于绿框内的点个数减去位于蓝框内的点个数就行了。对于函数在框内的整点个数,有如下规律。
绿色和蓝色线是完全包括在框内,则整点个数分别为为 \(4\times a,4\times b\)。
而紫色线只有一部分在框内,则整点个数为 \(4\times(b\times2-c+1)\)。
那么接下来的事情就好办了,我们只需要求出两个框内对应点的个数,相减,可以一次性得到当前权值下所有点,直到出现了 \(k\) 个点为止。
代码:
#include <bits/stdc++.h>
using namespace std;
long long arr[10];
long long k;
long long count(long long w,long long len){
if(len==0||w>len*2)return 0;
//如果当前框长度为0表示w权值在第四层,没有第五层。
//如果fabs(x)+fabs(y)大于len*2表示与当前框根本没有交点
if(w==0)return 1;
//权值为0的情况只有(0,0),一个点
if(w<=len)return 4*w;
//上图绿色蓝色线情况
return 4*(len*2-w+1);
//上图紫色线情况
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%lld%lld%lld%lld%lld",arr+4,arr+3,arr+2,arr+1,&k);
//因为越后面的框越大,所以层越小,倒着输入
long long res=0;
long long w=0;
//权值从0开始遍历
while(k){
for(long long i=1;i<=4;i++){
if(w%i)continue;
//如果是i的倍数则继续,表示权值w是在i层覆盖得到的
long long cnt=count(w/i,arr[i])-count(w/i,arr[i+1]);
//算出位于i层而不位于i+1层的点的个数
res+=min(cnt,k)*w;
//加上权值*个数
k-=min(cnt,k);
//一直到k个点
}
w++;
//遍历下一个权值
}
printf("%lld\n",res);
}
return 0;
}