2020牛客寒假算法基础集训营2 题解

A-做游戏(思维)

ans=min(A,X)+min(B,Y)+min(C,Z)

 

#include<iostream>
#include<algorithm>
 using namespace std;
 typedef long long ll;
 ll min(ll a,ll b){ if(a>b) return b;return a;}
 ll a[3],b[3];
 int main()
 {
     for(int i=0;i<3;i++)    scanf("%lld",&a[i]);
     for(int i=0;i<3;i++)    scanf("%lld",&b[i]);
     ll ans=min(a[0],b[1])+min(a[1],b[2])+min(a[2],b[0]);
     cout<<ans<<endl;
  } 

 

B-排数字(思维题)

最佳的排列方法为616161616.....,设字符串中1的个数为x,6的个数为y,当y≥2的时候ans=min(y-1,x)

 

#include<iostream>
#include<algorithm>
 using namespace std;
 const int maxn=2e5+10;
 char s[maxn];
 int main()
 {
     int n,x=0,y=0;
     scanf("%d%s",&n,s);
     for(int i=0;i<n;i++){
         if(s[i]=='6')    x++;
        else if(s[i]=='1')    y++; 
     }
    int ans=0;
    if(x>=2)    ans=min(x-1,y);
    cout<<ans<<endl;
 }
 

 

G-判正误(快速幂)

直接计算mod1e9+7意义下等式左边的值判断是否等于右边即可

 

#include<iostream>
#include<algorithm>
 using namespace std;
 typedef long long ll;
 const int mod=1e9+7;
 ll pow(ll a,ll b)
 {
     ll res=1;
     while(b){
         if(b&1)    res=(a*res)%mod;
         b>>=1;
         a*=a;
         a%=mod;
     }
    return res;
 }
 int main()
 {
     int n;
     scanf("%d",&n);
    while(n--){
        ll a,b,c,d,e,f,g;
        cin>>a>>b>>c>>d>>e>>f>>g;
        ll ans=pow(a,d)%mod;
        ans+=pow(b,e)%mod;
        ans+=pow(c,f)%mod;
        if(ans==g)    cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }     
 }
 

 D-数三角(计算几何)

枚举三个顶点,判断两条边的点积是否小于0即可,注意判断三点共线的情况

#include<iostream>
#include<algorithm>
 using namespace std;
 typedef long long ll;
 const int maxn=505;
 ll n,x[maxn],y[maxn];
 bool judge(int a,int b,int c)
 {
     if((x[a]*y[b]-x[b]*y[a]+x[b]*y[c]-x[c]*y[b]+x[c]*y[a]-x[a]*y[c])==0)    return false;
     return (x[b]-x[a])*(x[c]-x[a])+(y[b]-y[a])*(y[c]-y[a])<0;
 }
 int main()
 {
     scanf("%lld",&n);
     ll ans=0;
     for(int i=0;i<n;i++) scanf("%lld%lld",&x[i],&y[i]);
    for(int i=0;i<n;i++){
        for(int j=i+1;j<n;j++){
            for(int k=j+1;k<n;k++){
                if(judge(i,j,k)||judge(j,k,i)||judge(k,i,j)) ans++;
            }
        }
    }
    cout<<ans<<endl;     
    return 0;
 }

 C-算概率(概率)

设f[i][j]为做i道题对j题的概率,①当j>0时,fi,j = fi-1,j * ( 1 - p[i] ) + fi-1,j-1 * p[i] ②当j=0时,fi,j = fi-1,j * ( 1 - p[i] ) 

 

#include<iostream>
#include<algorithm>
 using namespace std;
 typedef long long ll;
 const int maxn=2005;
 const int mod=1e9+7;
 ll p[maxn],f[maxn][maxn];
 int main()
 {
     int n;
     scanf("%d",&n);
     for(int i=1;i<=n;i++)    scanf("%lld",&p[i]);
     f[0][0]=1;
     for(int i=1;i<=n;i++){
         f[i][0]=f[i-1][0]*(mod+1-p[i])%mod;
         for(int j=1;j<=n;j++)
         f[i][j]=f[i-1][j]*(mod+1-p[i])%mod+(f[i-1][j-1]*p[i])%mod;
     }
    for(int i=0;i<=n;i++)    cout<<f[n][i]%mod<<" ";
 }

 E-统计数(数学)

对等式两边同时平方 : i + j + 2 * sqrt( i * j) =k,这样我们发现 i * j必须是完全平方数的时候才可以,因此我们枚举完全平方数,再枚举其因子数即可,每次都加2(因为 (2,8,4) 跟 (8,2,4)算两个)最后还要再减去1(减去(4,4,4)这种情况),

 

#include<iostream>
#include<algorithm>
#include<cmath>
 using namespace std;
 int main()
 {
     int n,ans=0;
     scanf("%d",&n);
     for(int i=1;i<=sqrt(n);i++){
         for(int j=1;j<=i;j++)
             if(i*i%j==0)    ans+=2;
         ans--;
     }
    cout<<ans<<endl;
 }

 F-拿物品(思维题,排序)

直接按照两物品的和排序依次输出即可

 

#include<iostream>
#include<algorithm>
 using namespace std;
 const int maxn=2e5+10;
 struct node{
     int a,b,sum,pos;
 }a[maxn]; 
 int cmp(node x,node y){return x.sum>=y.sum;}
 int main()
 {
     int n;
     scanf("%d",&n);
     for(int i=1;i<=n;i++)    scanf("%d",&a[i].a),a[i].pos=i;
     for(int i=1;i<=n;i++)    scanf("%d",&a[i].b),a[i].sum=a[i].a+a[i].b;
     sort(a+1,a+1+n,cmp);
     for(int i=1;i<=n;i+=2)    cout<<a[i].pos<<" ";
     cout<<endl;
     for(int i=2;i<=n;i+=2)    cout<<a[i].pos<<" ";
 }

 H-施魔法(DP)

先将数组排序,数组被连续的分成几段取完代价才能最小

定义 f i为取完前i个元素的花费

 

 

维护  的前缀最小值就能O(1)转移了。

 

 

#include<iostream>
#include<algorithm>
 using namespace std;
 const int maxn=3e5+7;
 int a[maxn],dp[maxn],pre,k,n;
 int main()
 {
     scanf("%d%d",&n,&k);
     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
     sort(a+1,a+1+n);
     pre=-a[1];
     for(int i=1;i<k;i++)    dp[i]=2e9;
     for(int i=k;i<=n;i++){
         dp[i]=pre+a[i];
         pre=min(pre,dp[i-k+1]-a[i-k+2]);
     }
    cout<<dp[n]<<endl;
 }

J-求函数(线段树)

题解链接:https://www.cnblogs.com/overrate-wsj/p/12274408.html

I-建通道(思维)

思路:对于相同的元素,连接起来的代价为0,所以我们可以先将所有元素去重。去重之后,我们从低到高遍历每个数字的每一位(二进制意义下),找到所有数字中既有0又有1的一位,将边将在0与1之间,这样的代价肯定是最小的

#include<iostream>
#include<algorithm>
 using namespace std;
 typedef long long ll;
 const int maxn=2e5+10;
 ll a[maxn];
 int main()
 {
     int n;
     scanf("%d",&n);
     for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
     sort(a+1,a+1+n);
     int len=unique(a+1,a+1+n)-(a+1);
     for(int i=0;i<=30;i++){
         ll ans=1<<i;
        int x=0,y=0;
        for(int j=1;j<=len;j++){
            if((a[j]>>i)&1)    x++;
            else    y++;
        } 
        if(x&&y){
            ans*=(ll)(len-1);
            cout<<ans<<endl;
            return 0;
        }
     }
     cout<<0<<endl;
     return 0;
 }

 

posted @ 2020-02-06 17:28  overrate_wsj  阅读(163)  评论(0编辑  收藏  举报