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

A.做游戏

题目描述:
牛牛和 牛可乐进行了多轮游戏, 牛牛总共出了 A 次石头,B 次剪刀,C 次布;牛可乐总共出了 X 次石头,Y 次剪刀,Z 次布。 你需要求出牛牛最多获胜多少局。

#include<bits/stdc++.h>
using namespace std;
int main(){
    long long a,b,c,x,y,z;
    cin>>a>>b>>c;
    cin>>x>>y>>z;
    cout<<min(a,y)+min(b,z)+min(c,x)<<endl;
    return 0;
}

B.排数字

题目描述
子串是连续的。
牛可乐最喜爱的字符串是616。
牛可乐得到了一个纯数字的字符串S,他想知道在可以任意打乱S顺序的情况下,最多有多少个不同的子串为616。
例如:"11451492616",有一个子串为 "616"
思路:
求出6和1的个数,放一个6在第一个,然后一直构造16,则答案为\(min(num[6]-1,num[1])\)

#include<bits/stdc++.h>
using namespace std;
char s[200010];
int main(){
    int n;
    cin>>n;
    cin>>(s+1);
    int n6=0,n1=0;
    for(int i=1;i<=n;++i){
        if(s[i]=='6') n6++;
        if(s[i]=='1') n1++;
    }
    n6--;
    cout<<min(n6,n1);
    return 0;
}

C.算概率

题目描述
牛牛刚刚考完了期末,尽管牛牛做答了所有n道题目,但他不知道有多少题是正确的。
不过,牛牛知道第i道题的正确率是\(p_i\)
牛牛想知道这n题里恰好有\(0,1,2,…,n\)题正确的概率分别是多少,对\(10e9+7\)取模。给出的概率也在\(10e9+7\)取模。
思路:dp很好想dp[i][j]表示在1~j道题种恰好答对了i道。dp[i][j]=dp[i-1][j-1]*p[i]+dp[i][j-1]*(1-p[i])%mod
但是要注意答对p[i]是在%mod下的概率,那么答错的概率为 (mod+1-p[i]) 。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=2010;
long long p[maxn];
long long dp[maxn][maxn];
int main(){
    int n;
    scanf("%d",&n);
    dp[0][0]=1;
    for(int i=1;i<=n;++i){
        scanf("%d",&p[i]);
        dp[0][i]=dp[0][i-1]*(mod+1-p[i]);
        dp[0][i]%=mod;
    }
    for(int i=1;i<=n;++i){
        for(int j=i;j<=n;++j){
            dp[i][j]=((dp[i-1][j-1]*p[j])%mod+dp[i][j-1]*(mod+1-p[j])%mod)%mod;
        }
    }
    for(int i=0;i<=n;++i){
        cout<<dp[i][n]<<" ";
    }
    return 0;
}

D.数三角

题目描述
牛牛得到了一个平面,这个平面上有 n 个不重合的点,第i个点的坐标为\((x_i,y_i)\)
牛牛想知道,这n(n<=500)个点形成的三角形中,总共有多少个钝角三角形。
思路:
枚举三个点,判断是否能组成三角形(平角三角形不算),且是有一个钝角。

#include<bits/stdc++.h>
using namespace std;
long long  x[601],y[601];
long long getdis(int i,int j){
    return (x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
}
long long dis[601][601];
bool judge(int i,int j,int k){
    if((y[j]-y[i])* (x[k]-x[j]) == (y[k]-y[j]) * (x[j]-x[i])){
        return 1;
    }
    return 0;
}
bool check(int i,int j,int k){
    long long a=dis[i][j];
    long long b=dis[j][k];
    long long c=dis[i][k];
    long long p=(a+b+c);
    if(judge(i,j,k)) return false;
    if(a+b<c||a+c<b||b+c<a){
        return true;
    }
    return false;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%lld%lld",&x[i],&y[i]);
    }
    for(int i=1;i<=n;++i){
        for(int j=i+1;j<=n;++j){
            dis[i][j]=getdis(i,j);
        }
    }
    long long ans=0;
    for(int i=1;i<=n;++i){
        for(int j=i+1;j<=n;++j){
            for(int k=j+1;k<=n;++k){
                if(check(i,j,k)){
                    ans++;
                }
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

E.做计数

题目描述
求有多少个不同的正整数三元组(i,j,k)满足:\(\sqrt{i}\)+\(\sqrt{j}\)=\(\sqrt{k}\),且\(i×j≤n\)
思路:左右平方可得:i+j+2×\(\sqrt{i×j}\)=k
,i×j可以开平方说明是平方数,那么就枚举1~n所有的平方数,再枚举平方数所有的i×j的因子对数。

#include<bits/stdc++.h>
using namespace std;
long long check(long long x){
    long long ans=0;
    for(int i=1;i*i<=x;++i){
        if(x%i==0){
            if(i*i==x){
                ans+=1;
            }
            else ans+=2;
        }
    }
    return ans;
}
int main(){
    long long n;
    scanf("%lld",&n);
    long long ans=0;
    for(long long i=0;i*i<=n;++i){
        ans+=check(i*i);
    }
    cout<<ans<<endl;
    return 0;
}

F.拿物品

题目描述:牛牛和牛可乐面前有n个物品,这些物品编号为1,2,3...n,每个物品有两个属性a,b
牛牛与牛可乐会轮流从剩下物品中任意拿走一个,牛牛先选取。设牛牛选取的物品编号集合为H,牛可乐选取的物品编号的集合为T,取完之后,牛牛 得分为H内物品a属性之和,而牛可乐的得分为T内物品b属性之和,牛牛和牛可乐都希望自己的得分尽量比对方大(即最大化自己与对方得分的差)。你需要求出两人都使用最优策略的情况下,最终分别会选择哪些物品,若有多种答案或输出顺序,输出任意一种。
思路:假设当前H内a属性之和为A,T内b属性之和为B,牛牛下一步拿了i物品,牛可乐下一步拿了b物品,此时差为A+ai-(B+bj)。若两者交换i,j物品差为A+aj-(B+bi)。两次差值之差为(ai-bj)-(bj-ai)。若交换能使牛牛的优势变小(即证明牛牛拿i是正确的)则ai+bi>aj+bj。
所以两个人都优先拿ai+bi大的。

#include<bits/stdc++.h>
using namespace std;
struct ac{
    long long v,id;
}arr[200010];
bool cmp(ac t1, ac t2){
    return t1.v>t2.v;
}
long long a[200010],b[200010];
vector<int> ans1,ans2;
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%lld",&a[i]);
        arr[i].id=i;
    }
    for(int i=1;i<=n;++i){
        scanf("%lld",&b[i]);
        arr[i].v=abs(a[i]+b[i]);
    }
    sort(arr+1,arr+1+n,cmp);
    for(int i=1;i<=n;){
        if(i<=n){
            ans1.push_back(arr[i].id);
            ++i;
        }
        if(i<=n){
            ans2.push_back(arr[i].id);
            ++i;
        }
    }
    for(int i=0;i<ans1.size();++i){
        cout<<ans1[i]<<" ";
    }cout<<endl;
    for(int i=0;i<ans2.size();++i){
        cout<<ans2[i]<<" ";
    }
    return 0;
}

G.判正误

题目描述
牛可乐有七个整数 a,b,c,d,e,f,g并且他猜想 \(a^d+b^e+c^f=g\)。但牛可乐无法进行如此庞大的计算。请验证牛可乐的猜想是否成立。
思路:
快速幂计算,并且多次用不同的质数做模减小误差。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod=1e9+7;
ll qp(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1){
            res=res*a%mod;
        }
        a=a*a%mod;
        b/=2;
    }
    return res;
}
int m[] = {2, 3, 5, 7, 11, 31, 71, 97, 233, 397, 433, 449, 607, 857, 10007, 21179, 36251, 44579, 62003, 72883, 97843, 139991, 232013, 369353, 681521, 692711, 777241, 822821, 1956761, 2145137, 2915837, 6229117, 7788787, 13743493, 17331841, 19260817, 19269293, 19959809, 21006959, 23937083, 24410849, 28452757, 28478603, 29229359, 35570827, 35604011, 35875487, 37370863, 38303347, 38475517, 38819149, 40455791, 44021539, 45641993, 46531301, 48866749, 50529641, 52634191, 52790587, 55180799, 56971613, 58259351, 60954737, 62207269, 63367453, 65072599, 66017821, 67952779, 69475349, 74689217, 77059907, 77907121, 79391659, 84768797, 85584601, 85724879, 85756609, 86850899, 91783511, 92331541, 94519499, 96375241, 99033413, 99486311, 100569829, 106873549, 109329881, 109913681, 111186487, 111894067, 112136617, 112417363, 114011921, 119143363, 122994493, 123747781, 124001021, 126515639, 128191039, 128767909, 132222763, 133587661, 139644719, 145641527, 153388423, 155187077, 156883333, 157989581, 159538063, 161488643, 164039129, 166070447, 169181543, 169554227, 173564801, 175742867, 185469637, 187203899, 191263223, 198691817, 204144887, 211631201, 217903877, 218028203, 220073423, 228143453, 228667423, 232064653, 240519263, 245647159, 247586411, 247936121, 250949197, 253413211, 253464329, 260572729, 260590409, 262887773, 265711423, 266763641, 273585149, 276472817, 276500531, 280543667, 280649591, 281385491, 291366337, 293273159, 296973107, 302890501, 306568693, 315614297, 316729409, 317617121, 320337781, 320613497, 321322823, 324691051, 325963067, 327184157, 329900633, 330670159, 332058781, 332213669, 332300869, 334382221, 341895677, 347938237, 349011827, 349347503, 349906439, 353796941, 364557253, 364755931, 367946441, 372413831, 374358983, 379589897, 381149689, 389431873, 404683493, 405216109, 405495029, 408142403, 408989747, 410841979, 410935093, 412405351, 412592459, 412722139, 412990573, 418171483, 421270357, 424233613, 427938449, 428492083, 429962881, 430883569, 434988383, 435941201, 438816151, 440052953, 440143589, 444693631, 453646433, 455847109, 456640189, 457911511, 458185237, 463116761, 463861417, 469275953, 471298573, 471712513, 478267417, 483824813, 494828483, 497397293, 499657393, 507957479, 512906621, 519346459, 519879973, 520094713, 523213693, 525673273, 529575763, 529883803, 533887031, 534260809, 535328309, 541992667, 542253071, 544780177, 545567609, 552922529, 555129893, 555820037, 558473471, 563484017, 571310471, 578121241, 582251063, 583825639, 584121323, 592038487, 599098811, 601467677, 610073969, 615059213, 619220713, 622457177, 627412609, 630547919, 632342989, 637357363, 638865419, 648268013, 650007487, 651564761, 654115433, 661281713, 662664461, 667914281, 682988213, 691099121, 691445809, 692038043, 692411953, 698620943, 699007259, 701164631, 706806461, 707096251, 707697451, 709566589, 719095829, 725756807, 736880491, 739603867, 743026709, 744236861, 744396049, 747393791, 749395103, 760341121, 762934307, 773124059, 773195911, 776162609, 781629113, 781884613, 786120631, 788314343, 788898377, 788939293, 790209983, 791933183, 796328783, 798643889, 802280047, 803293991, 803847559, 809752739, 818520473, 820434047, 826810489, 829359959, 829707427, 836587463, 841011167, 843763253, 849410557, 851226437, 853058471, 853168793, 853778327, 859086391, 860720017, 863193077, 873061181, 888803059, 893035529, 900902953, 904636883, 917949577, 921817139, 922328707, 931449133, 933074827, 933156233, 935241721, 935632799, 939948881, 957119773, 961329913, 965269573, 965337949, 967551691, 971080093, 973578143, 976825877, 985100197, 985413691, 986124823, 990650057, 998244353, 999058883, 1000000007};
int main(){
    int T;
    cin>>T;
    while(T--){
        ll a,b,c,d,e,f,g;
        cin>>a>>b>>c>>d>>e>>f>>g;
        bool flag=1;
        for(int i=0;i<349;++i){
            mod=m[i];
            ll t1=qp((a%mod+mod)%mod,d);
            ll t2=qp((b%mod+mod)%mod,e);
            ll t3=qp((c%mod+mod)%mod,f);
            if(((t1+t2+t3)%mod+mod)%mod!=(g%mod+mod)%mod){
             flag=0;
             break;
            }
        }
        if(flag) puts("Yes");
        else
            puts("No");
    }
    return 0;
}

I.施魔法

题目描述:
牛可乐有n个元素(编号1..n),第i个元素的能量值为。
牛可乐可以选择至少k个元素来施放一次魔法,魔法消耗的魔力是这些元素能量值的极差。形式化地,若所用元素编号集合为 S,则消耗的魔力为S中元素的最大值和最小值之差。
思路:dp.状态转移方程为dp[i]=min(dp[j]-a[j+1]+a[i],dp[i])(j<=i-k),但是(n<=3e5)二维dp不行。可以利用迭代记录当前走到的i的前面(1~i-k)区间内最小的dp[j]-a[j+1];

#include<bits/stdc++.h>
using namespace std;
long long a[300010];
long long dp[300010];
int main(){
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;++i){
        cin>>a[i];
    }
    sort(a+1,a+1+n);
    long long pre=-a[1];
    for(int i=1;i<k;++i)
        dp[i]=1e18;
    for(int i=k;i<=n;++i){
        dp[i]=pre+a[i];
        pre=min(pre,dp[i+1-k]-a[i+1-k+1]);
    }
    cout<<dp[n]<<endl;
    return 0;
}

I.建通道

题目描述:
在无垠的宇宙中,有n个星球第i个星球有权值vi。
由于星球之间距离极远,因此想在有限的时间内在星际间旅行,就必须要在星球间建立传送通道。
任意两个星球之间均可以建立传送通道,不过花费并不一样。第i个星球与第j个星球的之间建立传送通道的花费是lowbit(v[i]⊕v[j]),其中⊕为二进制异或,而lowbit(x)为x二进制最低位1对应的值,例如lowbit(5)=1,lowbit(8)=8。特殊的lowbit(0)=0。
牛牛想在这n个星球间穿梭,于是――你需要告诉牛牛,要使这n个星球相互可达,需要的花费最少是多少。

思路:我借助lowbit和异或的性质考虑了一种贪心的思路。大概思想就是如果有一个数字的lowbit和其他数字都不同,那么所有数字都和它连接进行异或。
有几种情况:
1.全部一样(或全为0)。那么随便连接消耗肯定是0。
2.除0外全部一样且有0。此时所有相同数字进行异或,最后随便一个非0数字和0进行异或,消耗为一个非零lowbit。
3.除0外lowbit全一样且有0。那么如果两个非零异或,消耗肯定大于他们相等的lowbit(比如1010和1110,他们的lowbit为2,异或后的lowbit为4)。如果让他们全部和0异或那么都是最小的lowbit消耗了,那么消耗就是(非零数字个数×lowbit)。
4.lowbit全一样没有0。此时只能向右枚举位数,找到最低位和有其他数该位不同的数字,然后参考情况5。
5.lowbit有不一样。那么还是找到具有最小的lowbit的数,所有和它不同的数和它异或,而相同的数字和不同的数字作中间连接。比如(2(10),2(10),32(10000),第一个2和32连接消耗为2,第二个2也和32连接消耗也为2)。
还没看题解的方法,好像比我的方法简单很多。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
ll lb(ll x){
    return x&(-x);
}
int cnt;
struct ac{
   ll v,lb,id;
}a[maxn];
bool all(){
    for(int i=2;i<=cnt;++i){
        if(a[i].lb!=a[i-1].lb) return false;
    }
    return true;
}
map<int,bool> mmp;
int main(){
    int n;
    cin>>n;
    int v,cnt0=0;
    cnt=0;
    for(int i=1;i<=n;++i){
        cin>>v;
        if(v==0) {
            cnt0++;
            continue;
        }
        if(mmp[v]==0){
            a[++cnt].v=v;
            a[cnt].lb=lb(a[cnt].v);
            mmp[v]=1;
        }
    }
    if((cnt0==0&&cnt==1)||(cnt0==n)){ //全部一样(或全为0)
        cout<<0<<"\n";
        return 0;
    }
    if(cnt==1&&cnt0!=0){//除0外全部一样且有0
        int f;
        for(int i=1;i<=cnt;++i){
            if(a[i].v!=0){
                f=a[i].lb;
                break;
            }
        }
        cout<<f<<"\n";
        return 0;
    }
    ll s=0;
    if(all()&&cnt0!=0){//除0外lb全一样且有0
        int f;
        for(int i=1;i<=cnt;++i){
            if(a[i].v!=0){
                f=a[i].lb;
                break;
            }
        }
        cout<<cnt*f<<"\n";
        return 0;
    }
    while(all()&&cnt0==0){ //lb全一样且没有0
        s++;
        for(int i=1;i<=cnt;++i){
            a[i].v/=2;
            a[i].lb=lb(a[i].v);
        }
    }
    ll Min=20000000000;
    for(int i=1;i<=cnt;++i){
        if(a[i].lb==0) continue;
        Min=min(Min,a[i].lb);
    }
    for(int i=1;i<=s;++i){
        Min*=2;
    }
    cout<<Min*(cnt+(cnt0==0?0:1)-1)<<"\n";
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值