Codeforces Round #746 (Div. 2)简训

导语

日常

涉及的知识点

贪心,思维,排序,位运算,树,DFS序

链接:Codeforces Round #746 (Div. 2)

题目

A Gamer Hemose

题目大意:n把武器,每把武器有自己的伤害值,敌人有H的血量,每次遍历这n把武器,可以选择任意武器进行攻击,如果遍历一次后敌人血量大于0,重新遍历选择,不能连续两次选择同一把武器,询问最少攻击多少次能使敌人血量小于等于0

思路:选取最大值和次大值直接交替即可,直接除法运算

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4;
int t,n,h,a[maxn];
int main() {
    scanf("%d",&t);
    while(t--) {
        int ans=0,bigger=0,posber=0,big=0;
        scanf("%d%d",&n,&h);
        for(int i=1; i<=n; i++) {
            scanf("%d",a+i);
            if(a[i]>bigger) {
                bigger=a[i];
                posber=i;
            }
        }
        for(int i=1; i<=n; i++)
            if(a[i]>big&&i!=posber)
                big=a[i];
        int sum=big+bigger;
        ans=h/sum;
        h-=sum*ans;
        ans*=2;
        if(h>bigger)
            ans+=2;
        else if(h>0)
            ans++;
        printf("%d\n",ans);
    }
    return 0;
}

B Hemose Shopping

题目大意:给出n个乱序整数构成的序列,限定交换的步长至少为x,判断是否能经过有限次交换使得序列变成非递减序序列

思路:经过推导得出的结论:当步长≤n/2,序列中任意一个值可以到达任意位置,当步长大于n/2,序列中n-x+1–x是无法被交换的,也就是说这个区间内的所有元素必须一开始就是满足条件的,为了方便可以先将整个序列排序,之后判断对应区间内的元素有无变化即可

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,x,a[maxn],t,b[maxn];
int main() {
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d",&n,&x);
        for(int i=1; i<=n; i++) {
            scanf("%d",a+i);
            b[i]=a[i];
        }
        if(x<=(n/2)) {
            printf("YES\n");
            continue;
        }
        sort(b+1,b+1+n);//排成有序
        bool flag=0;
        for(int i=n-x+1; i<=x; i++)//区间判断
            if(a[i]!=b[i]) {
                printf("NO\n");
                flag=1;
                break;
            }
        if(!flag)printf("YES\n");
    }
    return 0;
}

C Bakry and Partitioning

题目大意:给定一棵树以及点权,判断是否能切1–k-1刀使得划分的部分各自异或和相等

思路:首先,异或和有一个这样的性质,如果异或和为0,那么必定是两个相同的数异或的结果,先求出整棵树的异或和,如果为0,则任意一刀都符合条件,如果不为0,则判断整棵树能否分为奇数个异或和相同的部分(或一个为最后结果,其余为0),详见代码

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int t,n,head[maxn],cnt,a[maxn],sum[maxn],res,ans,k;
bool vis[maxn];
struct node {
    int next,to;
} e[maxn];
void Add(int from,int to) {
    e[++cnt].to=to;
    e[cnt].next=head[from];
    head[from]=cnt;
}
void DFS(int u) {
    vis[u]=1;
    sum[u]=a[u];
    for(int i=head[u]; i; i=e[i].next) {
        int v=e[i].to;
        if(!vis[v]) {
            DFS(v);
            sum[u]^=sum[v];
        }
    }
    if(sum[u]==res) {
        ans++;
        sum[u]=0;
    }
    vis[u]=0;
}
int main() {
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d",&n,&k);
        res=ans=cnt=0;
        memset(head,0,sizeof(head));
        for(int i=1; i<=n; i++) {
            scanf("%d",&a[i]);
            res^=a[i];
        }
        for(int i=1; i<=n-1; i++) {
            int u,v;
            scanf("%d%d",&u,&v);
            Add(u,v);
            Add(v,u);
        }
        if(res==0) {
            printf("YES\n");
            continue;
        }
        DFS(1);
        if(ans>=3&&k>=3)
            printf("YES\n");
        else
            printf("NO\n");
    }
    //system("pause");
    return 0;
}

D Hemose in ICPC ?

题目大意:给出一个树,有边权,但边权未知,规定两点间距为两点路径边权值gcd,每次可以询问一个点集里的可获得的最大距离,求整棵树中能使得距离最大的两点

思路:本题比较特殊,与其说是求得距离,不如说是根据输入的数据构造距离,因为是取gcd,所以贪心的想,直接以相邻两点来计算便可获得最大值,也就是最后答案是相邻点,所以关键是求出最大值的两个端点,其余参考代码和参考文献,思路太巧了,看懂了之后觉得也不难,但是很棒

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int t,n,head[maxn],cnt,seq[maxn],top,ans;
bool vis[maxn];
vector<int>q;
struct node {
    int next,to;
} e[maxn];
void Add(int from,int to) {//链式前向星
    e[++cnt].to=to;
    e[cnt].next=head[from];
    head[from]=cnt;
}
void DFS(int u,int f) {//确定欧拉序
    for(int i=head[u]; i; i=e[i].next) {
        int v=e[i].to;
        if(v==f)continue;
        seq[++top]=v;
        DFS(v,u);
        seq[++top]=u;
    }
}
int Query() {//确定询问点集
    sort(q.begin(),q.end());
    q.erase(unique(q.begin(),q.end()),q.end());
    cout <<"? "<<q.size()<<" ";
    for(int i=0; i<q.size(); i++)cout <<q[i]<<" ";
    cout <<endl;
    q.clear();
    int t;
    cin >>t;
    return t;
}
int main() {
    int n;
    scanf("%d",&n);
    for(int i=1; i<n; i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        Add(u,v);
        Add(v,u);
    }
    DFS(1,0);
    for(int i=1; i<=n; i++)q.push_back(i);
    ans=Query();//获得整个的最大值
    int l=1,r=top;
    while(l<r-1) {//必须确定到两个端点
        int mid=(l+r)>>1;
        for(int i=l; i<=mid; i++)q.push_back(seq[i]);
        int t=Query();//获得区间最值
        if(t==ans)r=mid;//二分
        else l=mid;
    }
    cout <<"! "<<seq[l]<<" "<<seq[r]<<endl;
    return 0;
}

参考文献

  1. Codeforces Round #746 (Div. 2) D - Hemose in ICPC ?(交互 二分 欧拉序)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值