比赛链接:https://www.nowcoder.com/acm/contest/144#question
A | Singing 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;
}
}
C | Generation 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;
}
J | Heritage 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;
}