Codeforces Round #320 (Div. 2)

传送门:A. Raising Bacteria  (水题)

二进制中的1的个数,在数量上是恰好对应答案的。

#include <bits/stdc++.h>
#define pr(x) cout << #x << "= " << x << "  "
#define pl(x) cout << #x << "= " << x << endl;
#define Memset(x, a) memset(x, a, sizeof(x))
#define ll __int64
using  namespace  std;
const int inf=0x3f3f3f3f;
int  n;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int  main(){
    n=read();
    int cnt=0;
    while(n>0){
        if(n&1)cnt++;
        n>>=1;
    }
    cout<<cnt<<endl;
    return 0;
}

传送门:B. Finding Team Member   (模拟)

第一个数据 

1 2 
3 4 5 
想成
0 6 1 3 
6 0 2 4 
1 2 0 5 
3 4 5 0 
Aij表示的是i和j组队的力量值 找最大值是6 A12 
所以1选2,2选1 
然后不看第一行第一列第二行第二列再找最大值,最大值是5 A34 
所以3选4 ,4选3

#include <bits/stdc++.h>
#define pr(x) cout << #x << "= " << x << "  "
#define pl(x) cout << #x << "= " << x << endl;
#define Memset(x, a) memset(x, a, sizeof(x))
#define ll __int64
using  namespace  std;
const int inf=0x3f3f3f3f;
int  n;
int b[1000];
int a[1000][1000];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int  main(){
    n=read();
    for(int i=2; i<=2*n; i++){
        for(int j=1; j<i; j++){
            a[i][j]=read();
        }
    }
    int x,y;
    for(int  k=1; k<=n; k++){
        int maxn=0;
        for(int i=2; i<=2*n; i++){
            for(int j=1; j<i; j++){
                if(a[i][j]>maxn){
                    maxn=a[i][j];
                    x=i;y=j;
                    b[x]=y;b[y]=x;
                }
            }
        }
        for(int i=1; i<=2*n; i++){
            a[i][x]=0;a[x][i]=0;
            a[i][y]=0;a[y][i]=0;
        }
    }
    for(int i=1; i<=2*n; i++)printf("%d%c",b[i],i==2*n?'\n':' ');
    return 0;
}


传送门:C. A Problem about Polyline   (趣题)

题意:
   有从原点开始,斜率分别为1和-1的折线周期下去,问是否存在x使得整点(a,b)在折线上,存在的话求最小的x,无解输出-1.
 
思路: 由于斜率为1,所以x>=y.那么x<y的情况一定无解。
对于x>=y的情况:我们发现(a,b)点如果是落在斜率为1的折线上,那么该折线与x轴的交点为(a-b,0)
如果(a,b)点落在落在斜率为-1的折线上,那么该折线与x轴的交点为(a+b,0) 
分析可知,如果a+b为奇数,那么一定会落在斜率为-1的折线上,如果a-b为偶数,一定会落在斜率为1的折线上。
而(a+b)不为奇数和(a-b)不为偶数不可能同时成立(想想为什么)。
所以x>=y的情况一定有解。
二分答案即可(其实还有一种比较偷懒的办法是...不去考虑是否一定有解。把二分的初始条件设置为-1就好)
不过证明无解也并不难想。
#include <bits/stdc++.h>
#define pr(x) cout << #x << "= " << x << "  "
#define pl(x) cout << #x << "= " << x << endl;
#define Memset(x, a) memset(x, a, sizeof(x))
#define ll __int64
using  namespace  std;
const ll inf=0x3f3f3f3f;
int a,b;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

ll bin(ll l,ll r){
    ll res=-1;
    while(l<=r){
        ll m=(l+r)>>1;
        double  x=(a+b)*1.0/(2*m);
        if(x>=b)l=m+1,res=m;
        else r=m-1;
    }
    return  res;
}

int  main(){
    a=read();b=read();
    if(b>a)printf("-1");
    else{
        ll  k=bin(1LL,inf);
        printf("%.9lf\n",(a+b)*1.0/(2*k));
    }
    return 0;
}

方法二就是手推式子,  首先这题答案如果存在解x的话,点(a,b)落在斜率为-1的直线上面x最小
两段折线的方程分别是a-2kx=b,2kx-a=b。对于第二个式子,k=(a+b)/(2x)<=(a+b)/(2b),x=(a+b)/(2*k),其中k为整数,x为实数,所以第一个式子可以不考虑。(然后10行代码解决问题)
#include<bits/stdc++.h>
using  namespace  std;

int main(){
    int a,b;
    cin>>a>>b;
    if(b>a){ puts("-1");return 0; }
    printf("%.9lf",(a+b) / (2.0 * ((a+b) / (2*b)) ) );
    return 0;
}



传送门:D. "Or" Game     (好题)

  首先,一定是处理转化为二进制之后位数最多的几个数(连续乘以k次x),因为x至少为2,所以位数最多的几个数乘以x之后一定向左移动一位,肯定比其他的数大

其次,为什么不一定是处理最大的数呢,比如

2 1 2

9 12

如果处理12之后答案是25,而处理9之后答案是30,因为12的二进制左移一位之后会和9的二进制有重合,而9的二进制后移一位则不会有这种情况发生 

所以只要枚举处理这n个数中的哪个就行了,如果暴力的话最小是n*n*logK,

这里用了一点特殊的技巧,处理出了前缀的或和和后缀的或和,这样的话可以在n*logK(使用快速幂)的时间复杂度内完成(因为这题K很小,所以代码里就没有快速幂)

#include <bits/stdc++.h>
#define ll __int64
using  namespace  std;
const int inf=0x3f3f3f3f;
const  int  maxn=200010;
ll a[maxn],pre[maxn],nex[maxn];

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
  return x*f;
}

int  main(){
  int n,k,x;
  n=read();k=read();x=read();
  for(int i=1; i<=n; i++){
    a[i]=read();
    pre[i]=pre[i-1]|a[i];
  }
  for(int i=n; i>=1; i--){
    nex[i]=nex[i+1]|a[i];
  }
  ll ans=0;
  for(int i=1; i<=n; i++){
    for(int j=0 ;j<k ;j++){
      a[i]*=x;
      ans=max(ans,pre[i-1]|a[i]|nex[i+1]);
    }
  }
  cout<<ans<<endl;
  return 0;
}



传送门:E. Weakness and Poorness  (三分+最大子段和

题意:

题目定义了两个变量:
poorness 表示一个区间内和的绝对值
weakness 表示一个所有区间最大的poornesss
题目要求你求一个 x 使得
a1x,a2x,...,anx 这个序列的 weakness 最小
输出最小的 weakness

思路:

     设f(x)为取x时的最大子段和,f(x)是先减后增的,于是可以用三分法求最值

至于最大连续子序列和最小连续子序列的求法,只要2遍最大连续和就可以了,第1遍后将每个数取反,这样第2

遍也是一个最大连续和。

     还有一种方法是二分,随着x的递增,序列的最大连续子序列和肯定在减小,因为每个数都减小了,其最小连

续子序列和的绝对值肯定是增大趋势,都是单调的,于是将最大连续子序列的和和最小连续子序列和的绝对值分开记录,就可二分,会有点麻烦。

  ps:这题精度卡的严,1e-11会wa,1e-12会T,三分100次就过了,所以建议还是用for100次,比较保稳。

#include<bits/stdc++.h>
using   namespace  std;
const  double  eps=3e-12;
double a[200010],b[200010];
int n;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
  while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
  return x*f;
}

double MaxSum(double c[]){
  double  ans=0,tmp=0;
  for(int i=1; i<=n; i++){
    tmp+=c[i];
    if(tmp<0)tmp=0;
    ans=max(ans,tmp);
  }
  return ans;
}

double calc(double  x){
  for(int i=1; i<=n; i++){
    b[i]=a[i]-x;
  }
  double ans1=MaxSum(b);
  for(int i=1; i<=n; i++){
    b[i]=-b[i];
  }
  double ans2=MaxSum(b);
  return max(ans1,ans2);
}

double  ternary(){
  double l=-1e4,r=1e4;
  double lm,rm;
  for(int i=0; i<100; i++){  //或者是while(l+eps<r)
    double d=(r-l)/3.0;
    lm=l+d,rm=r-d;
    double  v1=calc(lm);
    double  v2=calc(rm);
    if(v1<v2)r=rm;
    else   l=lm;
  }
  return calc(r);
}

int  main(){
  n=read();
  for(int i=1; i<=n; i++){
    a[i]=read();
  }
  printf("%.15lf",ternary());
  return 0;
}

L:第一题的位运算,C题括号较多要多用空格隔开,D题的前后缀或和


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值