Codeforces Round #432 (Div. 2) 总结

这场…不仅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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值