Codeforces Round #432 (Div. 2)题解
A.Arpa and a research in Mexican wave
简单模拟题,略
B.Arpa and an exam about geometry
题意:
已知三个点的坐标,能否找到一个点和一个角度,使得整个坐标系绕这个点旋转这个角度之后A到B的位置,B到C的位置
思路:
首先如果三个点共线显然无解,否则它们构成一个三角形,找到这个三角形的外心,记做O,如果角AOB=角BOC则存在,反之不存在,考虑到角的比等于边的比,直接判断AB是否等于BC就行了
AC代码:
#include <bits/stdc++.h>
using namespace std;
long long x1,y1,x2,y2,x3,y3;
int main()
{
scanf("%I64d%I64d%I64d%I64d%I64d%I64d",&x1,&y1,&x2,&y2,&x3,&y3);
long long dx1=x2-x1,dx2=x3-x2,dx3=x3-x1;
long long dy1=y2-y1,dy2=y3-y2,dy3=y3-y1;
if(dx1*dy3==dx3*dy1)
printf("No\n");
else
{
long long dist1=dx1*dx1+dy1*dy1;
long long dist2=dx2*dx2+dy2*dy2;
long long dist3=dx3*dx3+dy3*dy3;
if (dist1==dist2)
printf("Yes\n");
else
printf("No\n");
}
}
C.Five Dimensional Points
题意:
在五维超空间下有若干个点,对于一个点\(a\)如果存在\(b、c\)使得角\(bac\)为锐角,那么称这个角是坏的,反之则是好的,给定若干个点,输出有多少个好点以及它们的编号
\(n \le 1000\)
思路:
暴力做的话复杂度\(O(n^3)\)
但是事实上,如果点数大于\(33\)一定不存在好点,证明:
将任意一个点作为\(a\)点转换为坐标原点,那么其他点的坐标正负的情况共有\(2^5=32\)种,如果点数大于\(32\)的话由容斥原理,一定存在两个点,设为\(b、c\)坐标的正负情况完全相同,那么考虑内积角\(bac\)一定为锐角
(官方题解说大于$2 \times $维数的话一定不存在,没有证明,直接类比推理……,我也不会证……)
所以大于\(33\)的情况直接输出0,否则暴力做就可以了
(然而,数据水,直接暴力做就能过了!!?,虽说感觉的确不好构造数据卡掉)
AC代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e3+7;
int dot[maxn][maxn];
int a[maxn],b[maxn],c[maxn],d[maxn],e[maxn];
int len[maxn];
vector <int> good;
int n;
bool flag=false;
bool check(int i)
{
for (int j=0;j<n;j++)
for (int k=0;k<n;k++)
{
if(j==i||k==i||j==k) continue;
if((a[j]-a[i])*(a[k]-a[i])+(b[j]-b[i])*(b[k]-b[i])+(c[j]-c[i])*(c[k]-c[i])+(d[j]-d[i])*(d[k]-d[i])+(e[j]-e[i])*(e[k]-e[i])>0)
{
//cout<<j<<" "<<k<<endl;
return true;
}
}
return false;
}
int main()
{
scanf("%d",&n);
for (int i=0;i<n;i++)
scanf("%d%d%d%d%d",&a[i],&b[i],&c[i],&d[i],&e[i]);
if(n>50)
{
printf("0\n");
return 0;
}
for (int i=0;i<n;i++)
if(!check(i)) good.push_back(i+1);
printf("%d\n",good.size());
for (int i=0;i<good.size();i++)
printf("%d\n",good[i]);
}
D. Arpa and a list of numbers
题意
给定一个序列a,我们可以对序列中的元素做一下两种操作:
(1)花费\(x\)删除这个元素
(2)花费\(y\)将这个元素\(+1\)
输出使得整个序列的\(gcd\)不为\(1\)的最小花费
\(n \le 5e5\) \(a_i \le 1e6\)
思路:
首先显然可以只考虑\(gcd\)是素数的情况,如果给定一个\(gcd\),那么我们可以在\(O(n)\)的时间内求出最小的花费,然而数据范围内大概有\(70000\)个素数,不能枚举
进而考虑一次处理一段区间,对于一个区间$ [k \times gcd , (k+1) \times gcd -1)]$ ,越接近右端点提升这个数到\((k+1) \times gcd\) 所需的花费越小,所以越靠近左侧删除越划算,越靠近右侧增加越划算,可以找到一个界,使得左侧都是删除,右侧都是增加,那么预处理后我们就可以\(O(1)\)地算出一个区间的花费了
推导后可以算出这个界是$ mid =k+ gcd- min (\frac{x}{y}-1,gcd)$ ,那么对于一段区间,代价是:
左端点到\(mid\)的数量 $\times x + \sum mid $到右端点 \((\)右端点\(-a_i) \times y\)
即\(cnt_{ij}\)为\(i\)到\(j\)的元素数,\(sum_{ij}\) 为值为\(i\)到\(j\)的元素之和,转化为
用前缀和预处理\(cnt\)和\(sum\)即可
时间复杂度
$ O(n \sum \frac{1}{p}) = O(nloglogn)$ (Mertens' theorems)
AC代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+7;
const int maxm=1e6+10;
int x,y,n;
int a[maxn],prime[maxm],cnt[maxm*2];
long long sum[maxm*2];
int tot=0,amax=0;
bool vis[maxm];
void init()
{
int m=(int)sqrt(1e6+0.5);
memset(vis,0,sizeof(vis));
for (int i=2;i<=m;i++)
if(!vis[i])
for (int j=i*i;j<=1e6;j+=i) vis[j]=true;
for (int i=2;i<=1e6;i++) if(!vis[i]) prime[++tot]=i;
for (int i=0;i<=2e6;i++)
cnt[i]+=cnt[i-1],sum[i]+=sum[i-1];
}
int main()
{
scanf("%d%d%d",&n,&x,&y);
for (int i=0;i<n;i++) scanf("%d",&a[i]),cnt[a[i]]++,sum[a[i]]+=a[i],amax=max(amax,a[i]);
init();
long long ans=0x3f3f3f3f3f3f3f3fll;
for (int j=1;j<=tot;j++)
{
int gcd=prime[j];
long long ret=0;
int mid=gcd-min(x/y+1,gcd);
for (int i=0;i<amax;i+=gcd)
{
int p=i+gcd;
ret+=(long long)x*(cnt[i+mid]-cnt[i]);
ret+=(1ll*(cnt[p]-cnt[i+mid])*p-(sum[p]-sum[i+mid]))*y*1ll;
}
ans=min(ret,ans);
}
printf("%I64d\n",ans);
}