北京信息科技大学第十二届程序设计竞赛暨ACM选拔赛(同步赛)题解

A 爱丽丝的人偶(一)

思路: 先排小的,然后插空排大的就好了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=0x3f3f3f3f;
int a[1000007];
int main()
{
    
    
	int  n;
	cin>>n;
	int e;
	if(n%2==0){
    
    
	e=n/2;	
	}else{
    
    
		e=n/2+1;
	}
	
	int cnt=1;
	for(int i=1;i<=e;i++){
    
    
		a[cnt]=i;
		cnt+=2;
	}
	cnt=2;
	for(int i=e+1;i<=n;i++){
    
    
		a[cnt]=i;
		cnt+=2;
	}
	for(int i=1;i<=n;i++){
    
    
		if(i==1)printf("%d",a[i]);
		else printf(" %d",a[i]);
	}
	return 0;
}

B 爱丽丝的人偶(二)

思路: 找出有几个种人偶(用map),然后在m种人偶中取k个,是组合数,记得触除法的时候求逆元,组合数m!/((m-k)!*k!);

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
map<ll,ll>mp;
ll sum[1000007];
ll qpow(ll x,ll y){
    
    
	ll ans=1;
	while(y>0){
    
    
		if(y&1)ans=ans*x%mod;
		y/=2;
		x=x*x%mod;
	}
	return ans;
}
int main()
{
    
    
	ll n,k;
	cin>>n>>k;
	int cnt=0;
	for(int i=1;i<=n;i++){
    
    
		ll x;
		cin>>x;
		if(mp[x]==0){
    
    
			mp[x]++;
			cnt++;
		}else{
    
    
			mp[x]++;
		}
	}
	sum[0]=1;
	for(int i=1;i<=n;i++){
    
    
		sum[i]=sum[i-1]*i%mod;
	}
	ll ans;
	if(cnt>=k)
		 ans=sum[cnt]*qpow(sum[cnt-k]*sum[k]%mod,mod-2)%mod;
	else
		ans=0;
	printf("%lld\n",ans);
	return 0;
}

C 打毛玉大赛

思路: 只有一只血是1一只血是2的时候输出A,其他都是输出B,可以自己模拟一下。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=0x3f3f3f3f;
int main()
{
    
    
	ll a,b;
	cin>>a>>b;
	if(a==1&&b==1){
    
    
		printf("B\n");
	}else if((a==1&&b==2)||(a==2&&b==1)){
    
    
		printf("A\n");
	}else{
    
    
		printf("B\n");
	}
	return 0;
}

D 贪玩的二小姐(续)

思路: 如果是奇数个括号那么肯定不行,输出-1,如果是偶数就模拟一下,看看剩下几个‘(’和几个‘)’,如果是奇数个的就必须有一个要和另一种括号配对,这个括号要变,如果是偶数个就可以内部配对,两个同类括号只用变一个括号就可以完成配对。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char a[1000007];
int main(){
    
    
	int n;
	cin>>n;
	scanf("%s",a+1);
	int cnt1=0,cnt2=0;
	for(int i=1;i<=n;i++){
    
    
		if(a[i]=='('){
    
    
			cnt1++;
		}else if(a[i]==')'&&cnt1!=0){
    
    
			cnt1--;
		}else{
    
    
			cnt2++;
		}
	}
	if(n%2==1)printf("-1\n");
	else{
    
    
		int ans=0;
		ans+=cnt1/2;
		ans+=cnt2/2;
		if(cnt1%2==1)ans++;
		if(cnt2%2==1)ans++;
		printf("%d\n",ans);		
	}
	return 0;
} 

E 游戏机本当下手

思路: 先求出每一段的01有几个存下来,如果按一次的话就把每种情况都列一下,在每一部分里求和就好了。如果是多次的话只知道两边的数各有多少个,然后把两个数相乘,就是这一段的取法个数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll sum[1000007];
char a[1000007];
ll zhuan[1000007];
int main(){
    
    
	int n,k;
	cin>>n>>k;
	scanf("%s",a+1);
	int cnt=1;
	zhuan[cnt]=1;
	for(int i=2;i<=n;i++){
    
    
		if(a[i]!=a[i-1]){
    
    
			cnt++;
		}
		zhuan[cnt]++;
	}
	if(k>cnt){
    
    
		printf("0\n");
	}else{
    
    
		ll ans=0;
		if(k==1){
    
    
			for(int i=1;i<=cnt;i++){
    
    
				ans+=(zhuan[i]+1)*zhuan[i]/2;
			}
		}else{
    
    
			for(int i=1;i<=cnt-k+1;i++){
    
    
				ans+=zhuan[i]*zhuan[i+k-1];
			}
		}	
		printf("%lld\n",ans);
	}
	return 0;
}

F 宵暗的妖怪

思路: 只要做个小dp,我加进去一个新数,那么我们现在只要比较把i-1变成取的数和不把i-1变成取的数哪个比较大就取哪个就行了。
f[i]=max(f[i-3]+a[i-1],f[i-1]);

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[100007];
ll a[100007];
int main(){
    
    
    ll n;
    cin>>n;
    for(int i=1;i<=n;i++){
    
    
        cin>>a[i];
         if(i>2){
    
    
            f[i]=max(f[i-3]+a[i-1],f[i-1]);
        }
    }
    printf("%lld\n",f[n]);
    return 0;
}

G 魔界伊始

思路 我们要判断最小的可能得到的数字是多少,因为过程于辗转相除法类似,所有就相当于求所有数的最大公倍数,我们先排序一下,然后找出最小的最大公倍数,只要查询的数的数是最小的最小公倍数的倍数,就一定能成功。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,q;
ll a[100007];
int main(){
    
    
	cin>>n;
	int flag=0;
	for(int i=1;i<=n;i++){
    
    
		cin>>a[i];
	}		
	ll minn=1e18+7;	
	sort(a+1,a+1+n);
	for(int i=2;i<=n;i++){
    
    
		ll x=__gcd(a[i],a[i-1]);
		minn=min(minn,x);
	}
	cin>>q;
	if(minn==1)flag=1;
	while(q--){
    
    
		ll e;
		cin>>e;
		if(flag==1||e%minn==0)printf("Yes\n");
		else printf("No\n");
	}
	
	return 0;
} 

H 芭芭拉冲鸭~

思路: 因为他保证形成一个树,所以我们只要bfs,找到每个点从所有子节点过来最长的两条边,然后把他们的和就是在这个小子树下最长的边,每次只要返回最长的边就行(单独的)。对于每一条形成的边判一下大小就行了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>s[100007];
int cc[100007];
ll maxx=0;
ll dfs(int root,int fa){
    
    
	if(s[root].size()==0){
    
    
		return 0;
	}
	ll maxn1=0,maxn2=0;
	for(int i=0;i<s[root].size();i++){
    
    
		int v=s[root][i];
		if(v==fa)continue;
		ll e=dfs(v,root);
		if(cc[v]!=cc[root]){
    
    
			if(e+1>maxn1){
    
    
				maxn2=maxn1;
				maxn1=e+1;		
			}
			else maxn2=max(maxn2,e+1);						
		}
	}
	maxx=max(maxx,maxn1+maxn2);
	return maxn1;
}
int main(){
    
    
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
    
    
		char x;
		cin>>x;
		if(x=='R'){
    
    
			cc[i]=1;
		}
		if(x=='G'){
    
    
			cc[i]=2;
		}
		if(x=='B'){
    
    
			cc[i]=3;
		}
	}
	for(int i=1;i<n;i++){
    
    
		int u,v;
		scanf("%d%d",&u,&v);
		s[u].push_back(v);
		s[v].push_back(u);
	}
	dfs(1,-1);
	printf("%lld\n",maxx);
	return 0;
} 

I 永远亭的小游戏

思路: 我们发现,易得每一次的乘积事实上就是a[i]sumbsumc,这时候所有乘积的和就很好算了,然后把和除以三个数组的大小相乘就行了。注意取模和逆元。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll a[1000007],b[1000007],c[1000007];
ll qpow(ll x,ll y){
    
    
	ll ans=1;
	while(y>0){
    
    
		if(y&1)ans=ans*x%mod;
		y/=2;
		x=x*x%mod;
	}
	return ans;
}
int main(){
    
    
	ll n,m,k;
	scanf("%lld%lld%lld",&n,&m,&k);
	ll x=0,y=0,z=0;
	for(ll i=1;i<=n;i++){
    
    
		scanf("%lld",&a[i]);
	}
	for(ll i=1;i<=m;i++){
    
    
		scanf("%lld",&b[i]);
		y+=b[i];
		y%=mod;
	}
	for(ll i=1;i<=k;i++){
    
    
		scanf("%lld",&c[i]);
		z+=c[i];
		z%=mod;
	}
	ll ans=0;
	for(int i=1;i<=n;i++){
    
    
		ans=(ans+a[i]*y%mod*z%mod)%mod;
	}
	printf("%lld\n",ans*qpow(n*m%mod*k%mod,mod-2)%mod);
	return 0;
}

K 玩具销售员

思路: 一次能检查两个物品,我们算出一共能检查多少个物品,然后再比较让我们查的物品数量就行了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=0x3f3f3f3f;
int a[1000007];
int main()
{
    
    
	int n,m,k;
	cin>>n>>m>>k;
	if(2*k>=m){
    
    
		printf("Yes\n");
	}else{
    
    
		printf("No\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45434743/article/details/109903555