Codeforces Round #432 (Div. 2) 总结

来源:互联网 发布:淘宝详情页html代码 编辑:程序博客网 时间:2024/05/29 15:54

这场…不仅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;}