Codeforces Round #665 (Div. 2)

时间:2022-07-24
本文章向大家介绍Codeforces Round #665 (Div. 2),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

简要介绍

A题考察逻辑能力,这种题可以仔细推敲一下,比较锻炼逻辑能力。

B题贪心构造,尽量别想太复杂,要不很容易绕不出来,可以分类讨论一下或者自己构造几个数组找找规律。

C题从gcd入手找到m的倍数可以任意交换位置这个结论就差不多做出来了。

D题一开始确实感觉条件限制有点多,这时候就需要逐一的分析,找到切入点之后这些限制条件就迎刃而解了。D题的那个贪心是比较抽象的,比赛时我一般都是大胆猜测,根据直觉来,赛后再找贪心的正确原因,毕竟很多时候很多题如果真的证明的话很费时间和脑子,所以该猜结论的时候就大胆猜测。

A.Distance and Axis

题意:

给出OX轴,给出A的坐标x=n和一个数K,是否存在点B使得OB的长度减去AB的长度的绝对值等于K,如果没有不存在B这个坐标的话就进行操作,操作每次可以让A的坐标左移或者右移一个单位,求最小操作数使得B坐标合法存在。

题目有一个隐形条件,OX轴,所以A和B的坐标都不能小于0,因此当B的坐标小于0时是不存在的,于是需要进行题目中移动A坐标的操作来满足。

思路

这个题过程稍微麻烦但是结论很简单,所以比较适合初学者锻炼逻辑思维。这个题初步可以从B在A左边和B在A右边两个方向进行分类讨论,然后画一画图分析即可。

#include<iostream>
#include<cstdio>
using namespace std;
int main(void){
  int T,n,i,K;
  cin>>T;
  while(T--){
    scanf("%d%d",&n,&K);
    if(n<=K)
    printf("%dn",K-n);
    else{
      if((K-n)%2==0)
      printf("0n");
      else
      printf("1n");
    }
  }
  return 0;
}

B. Ternary Sequence

题意:

有a,b两个数组都只有0,1,2三个数构成,a数组中的0,1,2的个数为x1,y1,z1,b数组中0,1,2,的为x2,y2,z2,c[i]的值由a[i]和b[i]决定,x1+y1+z1=x2+y2+z2>0。

ci=ai * bi,ai>bi

ci=0,ai=bi

ci=-ai * bi,ai<bi

现在可以对a和b数组进行重组,求c数组的和的最大值。

思路

只有ai为2,bi为1时ci为正,只有ai为1,bi为2时ci为负,其余都为0,所以贪心让(2,1)这样的搭配尽可能多,让(1,2)尽可能少,其余的无所谓。让(2,1)尽可能多就需要优先让a数组中的2和b中的1配对,则ans+=2*min(z1,y2),这时候如果是a中的2多也不能再加啥贡献了,如果是b中的1多同样也没啥用,所以再考虑让(1,2)对尽可能少,就需要让让b中的2尽可能和a数组中的0和2配对(如果2还剩余的话),那么如果b数组中的2比a数组中的0和2数量之和小那么就并不会产生负贡献,反之则产生相应的负贡献,剩余的东西根据守恒原则会两两配对且贡献为0所以不需要考虑。

#include<iostream>
#include<cstdio>
using namespace std;
int main(void){
  int a0,a1,a2;
  int b0,b1,b2;
  int T;
  cin>>T;
  while(T--){
    scanf("%d%d%d",&a0,&a1,&a2);
    scanf("%d%d%d",&b0,&b1,&b2);
    int ans=2*min(a2,b1);//产生正贡献 
    int eq;//a数组中剩余2的个数 
    if(a2>b1)
    eq=a2-b1;
    else
    eq=0;
    if(b2>a0+eq)
    ans-=2*(b2-(a0+eq));//产生负贡献 
    printf("%dn",ans);  
  }
  return 0;
}

C. Mere Array

题意:

如果gcd(a[i],a[j])==a数组中的最小值,则可以互相交换位置,交换可以进行无数次,求操作后的a数组可以非递减吗。

思路:

假设a数组中最小值为m,则a数组中能交换位置的只有值为m的倍数的数,而且任意两个为m的倍数的数可以通过m作为媒介进行交换,然后如果不是m的倍数的数明显和谁的gcd也不可能出来m,所以动不了,因此可以弄一个b数组复制a数组,然后b数组排序,b数组中为m的倍数的不用管,不是m的倍数的看是否和a数组中当前位置的数一样(即是否改变了位置),如果不一样说明这个数被迫改变了位置因此不行(有点像数学里的反证法)。

坑点:我一开始是把a数组中不是m的倍数的数弄了出来,然后判断单独判断这些数是不是非递减,但是这样其实是忽略了m的倍数的影响,这应该算是一个小坑点。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_N=101000;
const int INF=0x3f3f3f3f;
int a[MAX_N],b[MAX_N];
int main(void){
   int T,n,i;
   cin>>T;
   while(T--){
      scanf("%d",&n);
      int cnt=0;
      int m=INF;
      for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        m=min(m,a[i]);
      }
      for(i=1;i<=n;i++)
      b[i]=a[i];
      sort(b+1,b+n+1); 
      int flag=1;
      for(i=1;i<=n;i++){
        if(b[i]%m!=0){
          if(a[i]!=b[i]){
            flag=0;
            break;
          }
        }
      }
      if(flag)
      printf("YESn");
      else
      printf("NOn");
   }
   return 0;
}

D. Maximum Distributed Tree

题意:

给一个树,然后边权未知,需要你分配,分配原则是边权乘积为k,而且在满足边权为1的边尽可能少的前提下使得所有点之间距离和最大。

思路:

首先把每条边的贡献算出来,每条边贡献就是子树的点个数*(n-子树点个数),很明显预处理出来每条边的贡献之后,把贡献最大的边赋值为最大是最优的,这样就把K质因数分解,如果K的质因数个数小于等于n-1的话,根据边权为1的边最少原则,这些因数都得用,然后把这些因数按从大到小依次分配给贡献最大的丶次大的等等。而如果K的质因数个数大于n-1,那么可以把一些质因数合并,那考虑怎么合并最优。最优应该是把前面多余的几项全合并成1个,然后得到n-1个因数赋值给n-1条边。(有一个比较抽象的解释,可以考虑K分解出了n个质因数,而只有n-1条边,这样就多出来一个质因数,那么这个质因数放在哪里比较好呢,很明显是放在贡献最大的那条边上,因为贡献最大的那条边不但贡献最大而且分配给这条边的因数也是最大的,因此再把多出来的这个质因数给最大的那条边是最优的,因为他们之间是乘法!!!扩展一下如果多出来很多质因数同样适用)。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int MAX_N=101000;
const long long MOD=1e9+7;
vector<int>v[MAX_N];
long long size[MAX_N];
void dfs(int x,int fa){
 int i;
 size[x]=1;
 for(i=0;i<v[x].size();i++){
  int y=v[x][i];
  if(y==fa)
  continue;
  dfs(y,x);
  size[x]+=size[y];
 }
}
long long val[MAX_N],prime[MAX_N];
bool cmp(long long x,long long y){
 return x>y;
}
int main(void){
 int n,i,T,x,y,m;
 cin>>T;
 while(T--){
  scanf("%d",&n);
  for(i=1;i<=n;i++)
  v[i].clear();
  for(i=1;i<n;i++){
   scanf("%d%d",&x,&y);
   v[x].push_back(y);
   v[y].push_back(x);
  }
  dfs(1,0);
  for(i=2;i<=n;i++)
  val[i]=size[i]*(n-size[i]);
  sort(val+2,val+n+1,cmp);
  for(i=2;i<=n;i++)
  val[i]%=MOD;
  scanf("%d",&m);
  for(i=1;i<=m;i++)
  scanf("%lld",&prime[i]);
  sort(prime+1,prime+m+1,cmp);
  for(i=1;i<=m;i++)
  prime[i]%=MOD;
  long long ans=0;
  if(m>n-1){
   long long res=1;
   for(i=1;i<=m-n+2;i++)
   res=res*prime[i]%MOD;
   ans=(ans+res*val[2]%MOD)%MOD;
   for(i=3;i<=n;i++)
   ans=(ans+val[i]*prime[m-n+i]%MOD)%MOD;
  }
  else{
   for(i=m+1;i<n;i++)
   prime[i]=1;
   for(i=2;i<=n;i++)
   ans=(ans+val[i]*prime[i-1]%MOD)%MOD;
  }
  printf("%lldn",ans);
 }
 return 0;
}

温馨提示

如果你喜欢本文,请分享到朋友圈,想要获得更多信息,请关注ACM算法日常

点赞的时候,请宠溺一点