牛客网暑期ACM多校训练营(第六场) - (A,C,J)

比赛链接https://www.nowcoder.com/acm/contest/144#question

ASinging Contest

题意:有编号1~2^n的2^n个人参加唱歌比赛,每人有n首歌每首歌都有一个权值。每轮比赛,相邻的两人比赛,如果一个人选的歌比对手权值高那么它获胜,所以每轮淘汰一半的人。每人每首歌的权值都是公布的,每人都想参赛最多的轮次没问最后谁拿冠军。

解析:由于每个人都想参赛最多的轮次,所以两个人之间的比赛其中拥有的歌中最大权值较大的那个人获胜。而一个人能赢的时候只要选权值⼤于对⽅最⼤值的最⼩值,⼤的留在后⾯不会 更差。直接贪心模拟即可。

July_xunle代码(339ms)

//这个方法更好理解
#include <bits/stdc++.h>
using namespace std;
int n;
struct fuck
{
    int no;
    vector<int> v;
};
fuck cmp(fuck a,fuck b)//a,b比赛
{
    int maxa,maxb;
    int i;
    maxa=maxb=0;
    for(i=0;i<n;i++)
    {
        maxa=max(a.v[i],maxa);
        maxb=max(b.v[i],maxb);
    }
    if(maxa>maxb)
    {
        for(i=0;i<n;i++)
        {
            if(a.v[i]>maxb)
            {
                a.v[i]=0;
                return a;
            }
        }
    }
    else
    {
        for(i=0;i<n;i++)
        {
            if(b.v[i]>maxa)
            {
                b.v[i]=0;
                return b;
            }
        }
    }
    return a;
}
int main()
{
    int t,cnt=1;
    scanf("%d",&t);
    while(t--)
    {
        int i,j;
        queue<fuck> q;
        scanf("%d",&n);
        int N=1<<n;
        for(i=1;i<=N;i++)
        {
            fuck a;
            a.no=i;
            for(j=0;j<n;j++)
            {
                int val;
                scanf("%d",&val);
                a.v.push_back(val);
            }
            sort(a.v.begin(),a.v.end());
            q.push(a);
        }
        int res=1;
        while(q.size()!=1)
        {
            fuck a,b,c;
            a=q.front();
            q.pop();
            b=q.front();
            q.pop();
            c=cmp(a,b);//c获胜
            q.push(c);
            res=c.no;
        }
        printf("Case #%d: %d\n",cnt++,res);
    }
    return 0;
}

 AAAAAAAc代码(234ms):

#include<bits/stdc++.h>
using namespace std;

int t,a[1<<15][18],p[1<<15],m,num,pos;

int dfs(int x,int rt,int r)//比赛每人还剩r首歌,参赛者rt要去掉权值大于x的权值最小的一首歌
{
    int l=1,mid=-1,L=r;
    while(1)
    {
        if((l+r)/2==mid) break;
        mid=(l+r)/2;
        if(a[rt][mid]>x) r=mid;
        else l=mid;
    }
    if(a[rt][1]>x) r=1;
    for(int i=r;i<L;i++)//去掉第r个
    {
        a[rt][i]=a[rt][i+1];
    }
    return 0;
}

int main()
{
    int l,n;
    scanf("%d",&t);
    for(int zz=1;zz<=t;zz++)
    {
        scanf("%d",&n);
        l=n;
        m=(1<<n);//人数
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
                scanf("%d",&a[i][j]);

            sort(a[i]+1,a[i]+1+n);
            p[i]=i;//p记录剩余参赛者
        }
        pos=m;
        for(int ss=l;ss>0;ss--)//比赛轮数
        {
            num=0;
            for(int i=1;i<pos;i+=2)//两个人之间的比赛
            {
                if(a[p[i]][ss]>a[p[i+1]][ss])
                {
                    dfs(a[p[i+1]][ss],p[i],ss);//参数者p[i]胜利,他要去掉权值大于对方最大值的最小值
                    p[++num]=p[i];
                }
                else
                {
                    dfs(a[p[i]][ss],p[i+1],ss);
                    p[++num]=p[i+1];
                }
            }
            pos/=2;
        }
        cout<<"Case #"<<zz<<": "<<p[1]<<endl;
    }
}

 

CGeneration I

题意:初始有编号为1~N的N个集合(注意集合内元素相同的只保留一个),有N次操作,第i次操作要在编号为i~N的集合中插入一个数值x,x∈[1,M]。注意x可以重复取。所以N次操作下来会得到一个每个集合都赋好值的结果,问最多会有多少种结果。

解析:由于插入一个元素对整个后缀的集合有影响,所以N次操作后第N个集合中插入了N个元素(但是有重复的被去掉)。我们只需考虑最后第N个集合就是分析N 次操作。下面我们对第N个集合中有k中不同值进行分析。

  • 我们假设第N个集合去重前的N个位置中有k种不同的值,那么1≤k≤min(N,M),k种值的选择有C(M,k)种方案。
  • 在确定了k之后,我们需要确定每种颜色第一次出现的位置(每种颜色第一次出现的位置不同即是不同答案),在其后位置出现的相同颜色都是重复的,第一个位置的值一定是第一次出现,考虑剩下的k-1种值和剩下的N-1个位置,方案数乘上C(N-1,K-1)。
  • 最后K中值得排列数:K!。

那么答案就是∑ k=1~min(N,M)  C(M,K)*C(N-1,K-1)*K!],考虑N,M较大我们不能直接求组合数,那么我们可以先求出这个和式当第一项即K=1时,C(M,K)*C(N-1,K-1)*K!=M;而且在我们求出K=i-1时C(M,i-1)*C(N-1,i-2)*(i-1)!的值后,K=i时C(M,i)*C(N-1,i-1)*i! = C(M,i-1)*C(N-1,i-2)*(i-1)! * (m-i+1)*(n-i+1)/(i-1)。所以我们递推即可求出整个和式的值。然后别忘了各种取模就好。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=1e6+5;
const ll mod=998244353;

ll n,m,inv[MAXN];//inv[i]表示i的逆元

ll pow_mod(ll n,ll k)//快速幂,求(n^k)%mod
{
    ll res=1;
    while(k>0)
    {
        if(k&1)
            res=res*n%mod;
        n=n*n%mod;
        k>>=1;
    }
    return res;
}

void init_inv()//预处理i的逆元
{
    inv[1]=1;
    for(int i=2;i<MAXN;++i)
    {
        inv[i]=pow_mod(i,mod-2);
    }
}

int main()
{
    init_inv();
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&m);
        ll pre=0,ans=0,tm,tn;
        pre=m%mod;
        ans=pre;
        int U=min(n,m);
        for(int i=2;i<=U;i++)
        {
            tm=(m-i+1)%mod;
            tn=(n-i+1)%mod;
            pre=(((pre*tm%mod)*tn)%mod)*inv[i-1]%mod;
            ans=(ans+pre)%mod;
        }
        printf("Case #%d: %lld\n",++cas,ans);
    }
    return 0;
}
JHeritage of skywalkert

题意:生成n随机数a1~an,求 MAX1≤i<j≤n lcm(ai,aj)

解析:随机两个正整数互质的概率为6/(π*π) 。只需要选出前 100 ⼤的数平⽅暴⼒即可。

nth_element用法:nth_element(first,nth,last,compare),功能是将区间[first,last]中的第n大的元素放在第n个位置,而且[first,nth)中的元素都是不大于第n个元素的(但是无序),(nth, last]中的元素都是不小于第n个元素的(但是无序)。

这里第n大和第n个位置都是相对于目前区间[first,last]来说的。

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e7+5;

unsigned a[MAXN];
unsigned x,y,z;

unsigned tang()//随机生成a数组
{
    unsigned t;
    x ^= x << 16;
    x ^= x >> 5;
    x ^= x << 1;
    t = x;
    x = y;
    y = z;
    z = t ^ x ^ y;
    return z;
}

int main()
{
    int T,n;
    scanf("%d", &T);
    for(int cas=1;cas<=T;cas++)
    {
        scanf("%d%u%u%u",&n,&x,&y,&z);
        for(int i=0;i<n;i++) a[i]=tang();

        int len=min(n,100);
        nth_element(a,a+n-len,a+n);

        unsigned long long ans=0;
        for(int i=n-len;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
                ans=max(ans,(unsigned long long)a[i]/(unsigned long long)__gcd(a[i],a[j])*(unsigned long long)a[j]);
        }
        printf("Case #%d: %llu\n",cas,ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值