FYN OI奋斗之路2~

XJOI 奋斗群 群赛3

A - Kirill And The Game

题意

输入两个区间l-r、x-y和一个数k,判断是否能在第一个区间找出一个数a,第二个区间找出一个数b,使得a除以b等于k。

题解

遍历一遍第一个区间,找出是否能使a*k在第二个区间范围内,若能,输出“Yes“,否则输出”No“。

#include<bits/stdc++.h>
using namespace std;
int main(){
    long long int l,r,x,y,ans,k;
    cin>>l>>r>>x>>y>>k;
    for(int i=x;i<=y;i++){
        ans=i*k;
        if(ans>=l&&ans<=r){
            cout<<"YES";
            return 0;
        }
    }
    cout<<"NO";
    return 0;
}

B - Gleb And Pizza

题意

输入一个大圆半径r和外环宽度d,再输入n个小圆的相关参数:圆心的坐标(x,y)和半径l。求出不在外环上的小圆个数(原题用的比萨和香肠2333)

题解

由于数据比较小,依次判断每一个小圆是否符合要求即可,需要注意外环宽度为0,小圆半径为0的情况比较特殊。

#include<bits/stdc++.h>
using namespace std;
int main(){
    int a,b,n;
    int dis;
    int ans=0;
    int x[100000],y[100000],r[100000];
    scanf("%d %d %d",&a,&b,&n);
    b=a-b;
    for(int i=1;i<=n;i++){
        scanf("%d %d %d",&x[i],&y[i],&r[i]);
        dis=x[i]*x[i]+y[i]*y[i];
        if(b!=0){
            if(dis<=(a-r[i])*(a-r[i])&&dis>=(b+r[i])*(b+r[i])) ans++;
        }
        else{
            if(r[i]==0){
            if(a*a==dis) ans++;
            }
        }
    }
    cout<<ans;
}

C - Ilya And The Tree

题意

有一棵树,每个结点有一权值,求对于每个结点,根节点到该结点的所有数的最大公约数(对于每一条路径,可将一个数改为0,gcd(a,0)=a。

题解

建树用set存储每一条路径的gcd值,方法很简单但不太会建树所有拖了比较久。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=200100;
int ans[MAXN]={0},first[MAXN]={0},a[MAXN]={0},t[MAXN]={0},n,cnt;
bool visit[MAXN];
struct edge{
    int to,next;
}e[MAXN];
set <int> ans1[MAXN];
int gcd(int a,int b){
    int x;
    while(b!=0){
        x=a;
        a=b;
        b=x%b;
    }
    return a;
}
void dfs(int x){
    visit[x]=true;
    int k=first[x];
    ans[x]=gcd(ans[t[x]],a[x]);
    ans1[x].insert(ans[t[x]]);
    set <int>::iterator it=ans1[t[x]].begin();
    while(it!=ans1[t[x]].end()){
        ans1[x].insert(gcd(*it,a[x]));
        it++;
    }
    while(k!=0){
        if(!visit[e[k].to]){
            t[e[k].to]=x;
            dfs(e[k].to);
        }
        k=e[k].next;
    }
}
int main(){
    memset(visit,false,sizeof(visit));
    int x,y,k;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<n;i++){
        scanf("%d %d",&x,&y);
        e[++cnt].to=y;
        e[cnt].next=first[x];
        first[x]=cnt;
        e[++cnt].to=x;
        e[cnt].next=first[y];
        first[y]=cnt;   
    }
    ans[1]=a[1];
    ans1[1].insert(0);
    visit[1]=true;
    k=first[1];
    while(k!=0){
        t[e[k].to]=1;
        dfs(e[k].to);
        k=e[k].next;
    }
    set <int>::reverse_iterator it;
    for(int i=1;i<=n;i++){
        it=ans1[i].rbegin();
        printf("%d ",max(*it,ans[i]));
    }
    return 0;
}

D - Mike and gcd problem

题意

输入一串n个数组成的序列,要求求出最少的操作次数使得该数列的最大公约数大于1,每一次操作可将数字a[i]、a[i+1]用a[i]-a[i+1]、a[i]+a[i+1]。若无法完成,输出”No“。

题解

先判断初始的最大公约数是否大于1,若不不大于1,则要将其变为2,将奇奇变成偶偶需要一次操作,将奇偶或偶奇变成偶偶需要两次操作。所以先处理奇奇,然后再处理其他情况。用贪心写即可。

#include<bits/stdc++.h>
using namespace std;
int gcd(int num1,int num2){
    if(num1<0) num1=abs(num1);
    else if(num2<0) num2=abs(num2);
    int m1=max(num1,num2);
    int m2=min(num1,num2);
    if(num1==0||num2==0){
        return m1;
    }
    else{
        for(int i=m2;i>=1;i--){
            if(num1%i==0&&num2%i==0) return i;
        }
    }
}
int main(){
    int n,a[100001];
    scanf("%d",&n);
    int count=0,ans=0,flag=0;
    int num,temp;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(a[i]!=0) flag=1;
    }
    if(flag==1){
        for(int i=2;i<=n;i++){
            if(i==2)num=gcd(a[1],a[i]);
            else num=gcd(num,a[i]);
        }
        if(num!=1) printf("YES\n0");
        else{
            for(int i=1;i<=n-1;i++){
                if(a[i]%2==1&&a[i+1]%2==1){
                    count++;
                    temp=a[i];
                    a[i]=temp-a[i+1];
                    a[i+1]=temp+a[i+1];
                }
            }
        for(int i=1;i<=n;i++){
            if(a[i]%2==0&&a[i+1]%2==1){
                count=count+2;
                temp=a[i];
                a[i]=temp-a[i+1];
                a[i+1]=temp+a[i+1];
                temp=a[i];
                a[i]=temp-a[i+1];
                a[i+1]=temp+a[i+1];
            }
            if(a[i]%2==1&&a[i+1]%2==0){
                count=count+2;
                temp=a[i];
                a[i]=temp-a[i+1];
                a[i+1]=temp+a[i+1];
                temp=a[i];
                a[i]=temp-a[i+1];
                a[i+1]=temp+a[i+1];
                }
            }
            printf("YES\n%d",count);
        }
    }
    else printf("NO");
}

E - Bank Hacking

题意

有n个点,每个点有初始的实力,给出n-1条线使两点之间互相连接,之后攻击某一个点,第一个点只要求实力大于该点实力,攻击一个点后与改点相邻(直接相连)或半相邻(用两条线相连)的点实力会加1。第二个点需要与被攻击的点相邻。

题解

每个点的实力最多提升2次,因此只要可以分两类讨论:
1.若只有一个最大的点,只需枚举该点相邻的点即可,若所有的权值等于最大值-1的点都与该点相邻或半相邻,输出最大值即可,反之,输出最大值+1;
2.若最大的点不止一个,对于每个最大的点,枚举相邻与半相邻的点,若所有最大的点都互相相邻或半相邻,输出最大值+1,反之,输出最大值+2。

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9+10;
vector <int> a[1000000];
int power[10000000],start,end;
int main(){
    int n,m=-inf,temp;
    int count1=0,count2=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&power[i]);
        m=max(m,power[i]);
    }
    for(int i=1;i<=n-1;i++){
        scanf("%d %d",&start,&end);
        a[start].push_back(end);
        a[end].push_back(start);
    }
    for(int i=1;i<=n;i++){
        if(power[i]==m) {
            count1++;
            temp=i;
        }
        else if(power[i]==m-1) count2++;
    }
    if(count1==1){
        int cnt=0;
        for(int i=0;i<a[temp].size();i++){
            if(power[a[temp][i]]==m-1) cnt++;   
        }
        if(cnt==count2) printf("%d",m);
        else printf("%d",m+1);
    }
    else{
        bool flag=false;
        for(int i=1;i<=n;i++){
            int cnt=0;
            if(power[i]==m) cnt++;
            for(int j=0;j<a[i].size();j++){
                if(power[a[i][j]]==m) cnt++;
            }
            if(cnt==count1) flag=true;
        }
        if(flag) printf("%d",m+1);
        else printf("%d",m+2);
    }
    return 0;
}

总结

最终RANK:8

待提升

因数据范围问题导致的错误比较多,注意特殊情况的单独讨论。

FIGHTING!

                                                                        2017年9月5日
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值