Codeforces Round #746(Div.2)

 

目录

A. Gamer Hemose

B. Hemose Shopping

C. Bakry and Partitioning


A. Gamer Hemose

题意:Agent有n(2<=n<=1e3)种武器,第[公式]种武器的伤害值为[公式]。他将面对一个生命值为[公式]的敌人。Agent将进行一次或多次攻击,直至敌人死亡。(敌人的生命值小于等于0就死亡。)但是,Agent不能连续两次选择相同的武器。问Agent杀死敌人至少需要用多少次武器。

思路因为连续两次选同一个武器,所以我们选择最大的和次大的来回选。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx=1e3+10;
int a[maxx];
int main()
{
    int t,n,H;
    cin>>t;
    while(t--)
    {
        cin>>n>>H;
        for(int i=0;i<n;i++)
        {
            cin>>a[i];
        }
        sort(a,a+n);
        int s=a[n-1]+a[n-2];
        int cnt=H/s;//11/9
        int ans=2*cnt;
        if((H-s*cnt)>0)
        {
            if((H-s*cnt)>a[n-1])
            {
                ans+=2;
            }
            else
            {
                ans+=1;
            }
        }
        cout<<ans<<endl;
    }
}

B. Hemose Shopping

题意:Hemose有一个由n(1<=n<=1e5)个整数组成的数组。他希望Samez按照非递减的顺序对数组进行排序。并且要求:通过交换元素进行排序。并且每次选择的两个元素的下标[公式][公式]满足:[公式][公式]。问是否可能通过有限次交换操作使得数组排好序?

思路:当n=4,x=3时,【1,2,3,4】中2,3没有办法交换,移动不了。

           当n=5,x=3时,【1,2,3,4,5】中3没有办法交换。

序列中存在元素始终无法移动,这部分连续且在中间。和a[n]能交换的最大下标+1是这段的左边界,比这个下标小的都能与它交换。同理,能与a[1]交换的最小下标-1是右边界。如果这段区间的元素与排序后的数组相比不同,则不可能完成非递减排序。

注意:小标不能小于1,大于n

 int l=max(1,n-x+1);
 int r=min(n,x);

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx=1e5+10;
int a[maxx];
int b[maxx];
int main()
{
    int t,n,x;
    cin>>t;
    while(t--)
    {
        cin>>n>>x;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            b[i]=a[i];
        }
        sort(b+1,b+1+n);
        int l=max(1,n-x+1);
        int r=min(n,x);
        int f=0;
        for(int i=l;i<=r;i++)
        {
            if(a[i]!=b[i])
            {
                f=1;
                break;
            }
        }
        if(f==1)
            cout<<"NO"<<endl;
        else
            cout<<"YES"<<endl;
    }
}

C. Bakry and Partitioning

题意:

给定一颗有n个带点权结点[公式],以及[公式]条边的树。(结点从[公式])。现在要求在至少删去1条边,至多删去[公式]条边的情况下能否使得删去边后的森林满足:对于每一个连通块其所有点的点权异或和是否都相等。

思路:

设在不删边的情况下所有点的异或和为sum。

如果sum=0,那么删且仅仅删一条边即可保证两个连通块的值相等。

如果sum\neq 0

1.当k=1时,即将树分成两个连通块,假设每个连通块的异或和是a,则a\bigoplus a=0,a\bigoplus a=sum,与sum=0冲突。

2.三个及以上连通块的情况:对任意非负整数有:a\bigoplus a\bigoplus a=a,即:每三个相同权值异或和的连通块可以当成一个连通块。因此只考虑能否形成3个权值异或和相等,并且权值异或和为[公式]的连通块即可,即删两条边。

将一个树分成异或和相等的三份:

这个操作通过DFS当前结点now和其子树异或和得到。如果递归到某个结点其与其所有子节点的异或和为[公式]那么可分割块加1,当可分割块等于3的时候结束。

注意:

存无向图/树的方式

 vector<int>g[maxx];
 g[a].push_back(b);
 g[b].push_back(a);

 搜索每一部分子树,递归到叶子节点切割分块,将子节点的值都并到当前节点

  else//nt不是当前节点的父节点
  {
       dfs(nt,now);
       a[now]=a[now]^a[nt];
   }

 a[now]=0,把已经分割出去的块的异或总和置为0,才能不影响第二块,第三块的分割。

 if(a[now]==sum)
  {
       a[now]=0;
       cnt++;
  }

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx=1e5+10;
vector<int>g[maxx];
int a[maxx];
int sum,cnt;
int t,n,k;
void init()
{
    sum=0;
    cnt=0;
    for(int i=1;i<=n;i++)
        g[i].clear();
}
void dfs(int now,int pre)
{
   if(cnt==3)
        return;
    int nt;
   for(int i=0;i<g[now].size();i++)
   {
       nt=g[now][i];
       if(nt==pre)
           continue;
       else
       {
           dfs(nt,now);
           a[now]=a[now]^a[nt];
       }
   }
   if(a[now]==sum)
   {
       a[now]=0;
       cnt++;
   }
}
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n>>k;
        init();
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            sum=sum^a[i];
        }
        int a,b;
        for(int i=1;i<n;i++)
        {
            cin>>a>>b;
            g[a].push_back(b);
            g[b].push_back(a);
        }
        if(sum==0)
        {
            cout<<"YES"<<endl;
        }
        else
        {
            if(k==2)
            {
                cout<<"NO"<<endl;
            }
            else
            {
                dfs(1,0);
                if(cnt>=2)
                {
                    cout<<"YES"<<endl;
                }
                else
                {
                    cout<<"NO"<<endl;
                }
            }
        }
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值