这场…不仅B被hack少了500+分,最后D题还fst了……好惨啊QAQ..
最终排名:rank488..rating+=1,目前rating=1805…(233怎么说我也加了rating)
A. Arpa and a research in Mexican wave
题意:有n个观众,第一时刻第一个观众站起来,第二时刻第二个观众站起来….第k时刻第k个观众站起来,第k+1时刻第k+1个观众站起来并且第一个观众坐下……第n+1时刻,第n+1-k个观众坐下,直到d+k时刻,第n个观众坐下,求第t时刻站着的人数。
思路&&题解:水题一道..我们只要分为三种情况就行了。第一种为t<=k的情况,这种情况第t秒站起来的人数就是t个人了;第二种为t>=n的情况,此时第t秒站起来的人就是max((n+k-t),0)个;而第三种情况就是t>=k&&t<=n了,这种情况站起来的人就是k个..
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,k,t;
int main() {
cin>>n>>k>>t;
if(t<=k)
cout<<t<<endl;
else if(t>=n)
cout<<(n+k)-t<<endl;
else
cout<<k<<endl;
return 0;
}
B. Arpa and an exam about geometry
题意:给你三个点的坐标,标为a,b,c三个点,问你能否找到一个点,使得a点绕着这个点旋转可以到b点,b点绕着这个点旋转同样角度可以到c点。
思路&&题解:这题看起来有点麻烦,实际上只用判一下三点不共线且线段ab长度等于线段bc长度就行了(为防止精度误差,可以直接判长度平方)。
P.S.我刚开始写的时候就判了a,b的x,y坐标差是否等于b,c的坐标差。这样的话a(0,0),b(3,4),c(3,9)就可以Hack掉我..
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x[3],y[3];
ll getdis(ll a,ll b,ll c,ll d) {
return (a-c)*(a-c)+(b-d)*(b-d);
}
int main(){
ios::sync_with_stdio(false);
for(int i=0;i<3;i++)
cin>>x[i]>>y[i];
if(getdis(x[2],y[2],x[1],y[1])==getdis(x[0],y[0],x[1],y[1])&&(x[2]-x[1])*(y[0]-y[1])!=(x[0]-x[1])*(y[2]-y[1]))
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
return 0;
}
C. Five Dimensional Points
题意:给你n个五维的点,定义一个点为好点,即该点与其他所有点组成的角均不为锐角。
思路&&题解:题解(1): 因为根据三角形内角和180度的定理,我们可以知道,如果这个点与其中两个点的夹角为直角或钝角,那么另外两个角一定是锐角,所以可以直接去掉那两个点,而要是这个角是锐角的话,可以直接去掉这个角。这样均摊下来是O(n^2)的..(比赛时的思路就是这个)
题解(2):比赛结束后第二天,我看了看别人的题解,发现还有一种更简单的方法..观察二维和三维得到,二维上一个好点的周围至多只有4个点,三维上一个好点的周围至多只有6个点..所以五维上如果存在好点,它周围的点必定不多于10个,所以若n>11,我们可以直接输出0。而小于等于11的话直接n^3暴力就行了..
代码如下(题解1):
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
typedef long long ll;
ll a[maxn],b[maxn],c[maxn],d[maxn],e[maxn];
bool vis[maxn];
int n,tot=0;
inline ll calc(int i,int j,int k) {
int v,w,x,y,z,vv,ww,xx,yy,zz;
v=a[i]-a[j];
w=b[i]-b[j];
x=c[i]-c[j];
y=d[i]-d[j];
z=e[i]-e[j];
vv=a[i]-a[k];
ww=b[i]-b[k];
xx=c[i]-c[k];
yy=d[i]-d[k];
zz=e[i]-e[k];
return v*vv+w*ww+x*xx+y*yy+z*zz;
}
int main() {
ios::sync_with_stdio(false);
cin>>n;
memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i]>>c[i]>>d[i]>>e[i];
for(int i=1;i<=n;i++) {
if(vis[i])
continue;
for(int j=1;j<=n;j++) {
for(int k=j+1;k<=n;k++) {
if(i==j||i==k)
continue;
ll tmp=calc(i,j,k);
if(tmp<=0) {
vis[j]=1;
vis[k]=1;
break;
}
else {
vis[i]=1;
break;
}
}
if(vis[i])
break;
}
}
for(int i=1;i<=n;i++)
if(!vis[i])
tot++;
cout<<tot<<endl;
if(tot==0)
return 0;
for(int i=1;i<=n;i++)
if(!vis[i])
cout<<i<<" ";
cout<<endl;
return 0;
}
D. Arpa and a list of numbers
题意:给你一个长度为n的数组,你有两个操作:
①将某个数删掉,消耗为x;
②将某个数+1,消耗为y。
这个操作可以对某个数多次使用,而你的目的是让最后n个数的Gcd不为1,求最小消耗。
思路&&题解:我们可以知道Gcd是我们知道,Gcd的结果是素数是最好的,所以我们首先筛出1e6以内的素数。
然后考虑对应数字是删除还是增添。我们考虑X和Y两种操作之间的关系:
①如果X<=Y的话,我们就不用进行Y操作了;
②如果x>y,我们设定tmp=X/Y表示我们最多对一个数进行增添数字的次数,如果超过了这个次数,我们不如将其删除来的划算;
之后只要记录一下前缀和,然后枚举一下Gcd,然后乱搞一下就行了。(具体见我代码)。
P.S.我fst的原因是我特判所有数都是1的时候以为不能全部删光,然后就留了一个一要加一,这样花费有时候不是最优的..
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=500001;
const int maxp=2000001;
vector<int> prime;
int isprime[maxp],num[maxn];
ll sum[maxp],sx[maxp];
void getprime(){
for(int i=2;i<maxp;i++){
if(isprime[i]==0){
prime.push_back(i);
for(int j=i+i;j<maxn;j+=i)
isprime[j]=1;
}
}
}
ll gcd(ll a,ll b) {
return b?gcd(b,a%b):a;
}
int main(){
getprime();
// cout<<prime.size()<<endl;
int n;
ll x,y;
cin>>n>>x>>y;
ll gc=-1;
int ones=0,mx=0;
for(int i=1;i<=n;i++) {
cin>>num[i];
sum[num[i]]++;
mx=max(mx,num[i]);
ones+=(num[i]==1);
if(gc==-1)
gc=num[i];
else
gc=gcd(gc,num[i]);
}
if(ones==n) {
ll one=n*1LL*y;
ll two=n*1LL*x;
cout<<min(one,two)<<endl;
return 0;
}
if(gc>1) {
cout<<"0"<<endl;
return 0;
}
for(int i=1;i<=2000000;i++)
sx[i]=sx[i-1]+sum[i]*i,sum[i]+=sum[i-1];
int sz=(int)prime.size();
ll ans=1e18,now;
for(int z=0;z<sz;z++){
int p=prime[z];
now=0;
if(p>mx)
break;
for(int i=1;i<=(mx/p)+1;i++) {
int le=(i-1)*p,ri=i*p,mid=max(le,(int)(ri-(x/y)-1));
ll res=sum[ri]-sum[mid];
now+=(sum[mid]-sum[le])*x+(ri*res-(sx[ri]-sx[mid]))*y;
}
ans=min(ans,now);
}
cout<<ans<<endl;
return 0;
}
E. Arpa and a game with Mojtaba
题意:给你n个数,a1,a2,……,an,两人轮流从中改数,每次选一个素数p和一个正整数k,将a1到an中所有可以被p^k整除的数除p^k。当玩家在他的回合把所有数都变成1后(即下一个玩家不能选择p),该玩家就赢了。
题解:(这题题解来自于http://blog.csdn.net/kele52he/article/details/77864968)。由于p只能是素数,所以我们可以把a1~an全都分解质因数,显然不同的素数之间互不影响,所以说我们只要把每一个素数的SG值算出来然后异或就是答案了。接下来就要解决如何对单个素数求SG值。
首先我们要把a1到an的数组处理成因子只有当前的这个素数的数组。比如2,3,4,6,当p=2的时候,我们就把它处理成2^1,2^0,2^2,2^1,也就是1,0,2,1,因为a1到an中所有可以被p^k整除的数都要除p^k,所以对于因子p的个数相同的数我们是可以视为一个数的。所以当我选2^1的时候,1,0,2,1就变成了0,0,1,0。这两个1的变化在任何时候都是一样的,所以说这两个1可以看作一个1。那么我们可以根据这一点来定义一个state:state的第i位为真,当且仅当当前状态下存在一个数等于p^i。比如:1,0,2,1的state值就等于1+2=3。1,2,2,3,3,5的state值就等于1+2+4+16=23。
而对于每一个state,要从1到它的最大位数枚举k,对于每一个k,大于等于k的位数,都要减k(将a1到an中所有可以被p^k整除的数除p^k),比如1,2,2,3,3,5当k为3的时候后续状态就是1,2,2,0,0,2。
所以(state >> k) | (state&((1 << k-1) - 1))是state的后续状态。
然后这题就可以解出来了,需要注意的是要用map来存SG函数的值。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxl=100000;
typedef map<int,int>::iterator mit;
map<int,int> SG,num;
int n,x,tmp,ans=0,prime[maxl],tot=0;
bool notprime[maxl+1];
inline void init() {
notprime[1]=1;
for(int i=2;i<=maxl;i++) {
if(!notprime[i])
prime[++tot]=i;
for(int j=1;j<=tot&&i*prime[j]<=maxl;j++) {
notprime[i*prime[j]]=1;
if(i%prime[j]==0)
break;
}
}
}
inline int solve(int x) {
if(x==0)
return 0;
if(SG.count(x))
return SG[x];
map<int,int> vis;
int p=x,t=0;
while(p) {
p/=2;
t++;
}
for(int i=1;i<=t;i++)
vis[solve((x>>i)|(x&((1<<(i-1))-1)))]=1;
for(int i=0;;i++)
if(!vis[i])
return SG[x]=i;
}
int main() {
init();
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&x);
for(int pos=1;prime[pos]*prime[pos]<=x;pos++) {
tmp=0;
while(x%prime[pos]==0) {
x/=prime[pos];
tmp++;
}
if(tmp!=0)
num[prime[pos]]=num[prime[pos]]|(1<<(tmp-1));
}
if(x!=1)
num[x]|=1;
}
for(mit it=num.begin();it!=num.end();it++)
ans=ans^solve(it->second);
if(ans==0)
puts("Arpa");
else
puts("Mojtaba");
return 0;
}