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

A .爱丽丝的人偶(一)

链接:https://ac.nowcoder.com/acm/contest/8755/A
来源:牛客网

题目描述

爱丽丝有n个人偶,每个人偶的身高依次是 1,2,3,…n
现在她要将这n个人偶摆成一排。
但是人偶被设置了魔法。假设对一个非两端的(不在队首也不在队尾)人偶x而言,她相邻的两个人偶,一个比x高、一个比x矮,那么就会爆炸。
爱丽丝想找到一种摆法,使得所有人偶都不会爆炸。你能帮帮她吗?

输入描述:

一个正整数 (1<=n<=10^5)

输出描述:

满足要求的一种摆法。如果有多解,输出任意一种摆法即可。

示例1
输入
复制
3
输出
复制
1 3 2

说明

对于第二个人偶,她两边的两个人偶都比她矮,满足要求。
另外,[3 1 2]、 [2 1 3] 、[2 3 1]这三种摆法也都满足要求。输出这三种摆法也视为正确。

分析:

可以将序列变成波浪形,峰谷的个数为x
n为偶数:x=n/2;
n为奇数:x=n/2+1
峰顶的的个数:n-x
峰谷编号从1-x,峰顶编号从x+1-n

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
int main()
{
	int n;
	scanf("%d",&n);
	int x=n/2;
	int y;
	if(n%2==0) y=x;
	else y=x+1;
	printf("%d %d",1,1+y);
	for(int i=2;i<=x;i++)
	{
		printf(" %d %d",i,i+y);
	}
	if(y>x) printf(" %d",y);
	return 0; 
} 

B .爱丽丝的人偶(二)

链接:https://ac.nowcoder.com/acm/contest/8755/B
来源:牛客网

题目描述

爱丽丝有个n人偶,第 个人偶的型号为ai。
现在爱丽丝要拿出其中k个人偶,满足这k个人偶的型号互不相同。
爱丽丝想知道自己有多少多不同的方案数?
注:若两个人偶的型号相同,那么无论拿她们中的哪一个都是等价的。
请将方案数对1e9+7取模。

输入描述:

第一行输入两个正整数n和k,用空格隔开。 (1<=k,n<=10^5)
第二行输入n个正整数ai,代表这n个人偶的型号。 (1<=ai<=10^18)

输出描述:

一个整数,代表最终方案的数量对1e9+7取模的值。

示例1
输入
复制
5 2
1 2 3 2 1
输出
复制
3

说明

一共有{1,2}{1,3}{2,3}这三种不同的方案(型号组合)。
请注意,拿第一个和第三个、第三个和第五个最终的型号组合都是{1,3},被视为同一种方案。

分析:

先找出不重复的型号个数m,然后求组合数
在这里插入图片描述
计算的时候要用到逆元,记录的型号的时候,ai范围太大,数组存不下,所以用map。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const ll mod = 1e9+7;
map<ll,int> a;
ll fpow(ll x,ll y)
{
	ll ans=1,base=x%mod;
	while(y)
	{
		if(y&1) ans=(ans*base)%mod;
		base=(base*base)%mod;
		y>>=1;
	}
	return ans;
}
int main()
{
	int n;
	ll k;
	scanf("%d%lld",&n,&k);
	ll x;
	ll cnt=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&x);
		if(a.count(x)==0) 
		{
			cnt++;
			a[x]=1;
		}
	}
	ll ans=1,m=cnt;
	for(ll i=m;i>=m-k+1;i--)
	{
		ans=(ans*i)%mod;
	}
	ll y=1;
	for(ll i=1;i<=k;i++)
	{
		y=(y*i)%mod;
	}
	ans=ans*fpow(y,mod-2)%mod;
	printf("%lld",ans);
	return 0; 
} 

C .打毛玉大赛

链接:https://ac.nowcoder.com/acm/contest/8755/C
来源:牛客网

题目描述

灵梦和萃香正在神社进行打毛玉的比赛。
初始有2只毛玉,它们的血量为a和b。
两个人轮流行动。灵梦先手。
每回合灵梦可以使用封魔阵,对某一只毛玉造成1点伤害。而萃香能使用户隐山投,对某一只毛玉造成任意点伤害。
两人约定,击杀最后一只毛玉的一方获胜。
假设双方都足够聪明,谁会获得最终的胜利?

输入描述:

两个正整数a和b,用空格隔开。(1<=a,b<=10^9)

输出描述:

如果灵梦获胜,则输出一个字符’A’。
如果萃香获胜,则输出一个字符’B’。
示例1
输入
复制
1 2
输出
复制
A

说明

灵梦先攻击血量为2的毛玉,这时无论萃香击杀哪一只毛玉,灵梦都可以击杀另一只,所以灵梦必胜。
示例2
输入
复制
1 1
输出
复制
B
说明
无论灵梦击杀第一只还是第二只,萃香都能击杀另外一只毛玉。

分析:

只有当a=1,b=2或者a=2,b=1的时候A才会获胜。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
int main()
{
	int a,b;
	scanf("%d%d",&a,&b);
    if(a==1&&b==2||a==2&&b==1) printf("A");
	else
	{
		printf("B");
	}
	return 0; 
} 

D .贪玩的二小姐(续)

链接:https://ac.nowcoder.com/acm/contest/8755/D
来源:牛客网

题目描述

芙兰朵露拿到了一个只含有 ‘(’ 和 ‘)’ 这两种字符的字符串。芙兰想玩一玩这个字符串。

现在将字符串括号匹配定义如下:

空字符串: “” 是匹配的。
若字符串s是匹配的,那么 “(s)” 是匹配的。
若字符串s和t都是匹配的,字符串 “st” 是匹配的。
例如,"(()())" 是匹配的 , "())(()"则不是匹配的。
芙兰朵露每一次操作可以将括号翻转,即把左括号变成右括号,或者把右括号变成左括号。
她想知道将给定的字符串变成匹配的,需要最少的操作次数是多少?

输入描述:

第一行一个正整数,代表给定字符串的长度。(1<=n<=10^6)
第二行是一个长度为n的、仅含有 '( '和 ‘)’ 这两种字符的字符串。

输出描述:

如果能在有限次操作将字符串变成匹配的,请输出最少的操作次数。
否则输出-1
示例1
输入
复制
4
()))
输出
复制
1

说明

将第三个括号翻转,字符串变成 “()()” ,为匹配的。
示例2
输入
复制
1
(
输出
复制
-1

说明

翻转后字符串变成")",显然不可能匹配。

分析:

先将合理的()配对,在计算不合理的"(“和”)"的个数x和y。
1.x和y都等于0,则答案是0
2.x和y的和不是偶数,说明无论怎么变化都不能成功,答案是-1
3.如果x是偶数,则对“(”来说只需要变x/2次,否则需要变x/2+1次,y同理。因为偶数的话可以变化后和自身配对,奇数的话需要和对方的一个进行配对。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+10;
const ll mod = 1e9+7;
char a[N];
int main()
{
	int n;
	scanf("%d",&n);
	getchar();
	scanf("%s",a);
	int x=0,y=0;
	for(int i=0;i<n;i++)
	{
		if(a[i]=='(')
		{
			x++;
		}
		else
		{
			if(x>0)
			{
				x--;
			}
			else y++;
		}
	}
	int z=x+y;
	if(z==0) printf("0");
	else if(z%2!=0) printf("-1");
	else
	{
		int cnt=0;
		cnt=x/2;
		x=x%2;
		cnt+=y/2;
		y=y%2;
		cnt+=x+y;
		printf("%d",cnt);
	}
	return 0; 
} 

E .游戏机本当下手

链接:https://ac.nowcoder.com/acm/contest/8755/E
来源:牛客网

题目描述

藤原妹红拿到了一个游戏机,游戏机上有’1’和’0’两个按钮。
妹红发现,只要按住某个按钮不放,屏幕上就能一直不断打印那个字符。
比如对于"11111111100000000001111111111",只需要按住1、再按住0、再按住1就可以打印出来了。这样妹红最少只需要按3次按钮就可以打印这个字符串。
现在妹红拿到了一个01字符串,她想截取其中的一个子串,这个子串最少按k 次按钮就可以打印出来。
(01字符串指仅由字符’0’和字符’1’组成的字符串)
注意这里“最少按 k次”的含义是:按k 次可以打印出这个子串,但按k-1 次就一定打印不出这个子串。
妹红想知道,一共有多少子串符合要求?
注:一个字符串的子串为该字符串删掉前面和后面部分字符(也可以不删)生成的字符串。
两个子串只要在字符串中位置不同则认为是不同的(哪怕字符串相等)。

输入描述:

第一行两个正整数 n和 k,分别代表字符串的长度、和妹红按下按钮的次数。
第二行为一个仅由字符“0”和“1”组成的字符串。

输出描述:

妹红至少按 k次就能按出来的子串的数量。
示例1
输入
复制
6 2
001100
输出
复制
8

说明

注意到k=2,因此要寻找最少按2次就能打印的子串。
s[0,2]=“001”,妹红最少按2次就能按出来,先按0再按1。
s[0,3]=“0011”,妹红最少按2次就能按出来,先按0再按1。
s[1,2]=“01”,妹红最少按2次就能按出来,先按0再按1。
s[0,3]=“011”,妹红最少按2次就能按出来,先按0再按1。
s[2,4]=“110”,妹红最少按2次就能按出来,先按1再按0。
s[2,5]=“1100”,妹红最少按2次就能按出来,先按1再按0。
s[3,4]=“10”,妹红最少按2次就能按出来,先按1再按0。
s[3,5]=“100”,妹红最少按2次就能按出来,先按1再按0。
共有8个子串符合要求。
示例2
输入
复制
3 1
110
输出
复制
4

说明

注意到k=1,因此要寻找按1次就能打印的子串。
s[0,0]=“1”,妹红按住1就可以打印出来,只按了1次按钮
s[0,1]=“11”,妹红按住1就可以打印出来,只按了1次按钮
s[1,1]=“1”,妹红按住1就可以打印出来,只按了1次按钮
s[2,2]=“0”,妹红按住0就可以打印出来,只按了1次按钮
共有4个子串符合要求。
备注:
1<=n<=10^6

分析:

先计算连续的1和0有几个,再利用排列组合计算答案。
注意如果k=1,则ans=n+n-1+n-2+…+1(n为这个字符连续的个数)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+10;
const ll mod = 1e9+7;
char a[N];
ll b[N];
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	getchar();
	scanf("%s",&a);
	int k=0;
	char t;
	b[++k]=1,t=a[0];
	for(int i=1;i<n;i++)
	{
		if(a[i]==t) 
		{
			b[k]++;
		}
		else
		{
			
			b[++k]=1;
			t=a[i];
		}
	}
	ll ans=0;
	for(int i=1;i<=k-m+1;i++)
	{
		if(m!=1)
		ans+=b[i]*b[i+m-1];
		else ans+=(b[i]+1)*b[i]/2;
	}
	printf("%lld",ans);
	return 0; 
} 

F .宵暗的妖怪

链接:https://ac.nowcoder.com/acm/contest/8755/F
来源:牛客网

题目描述

露米娅作为宵暗的妖怪,非常喜欢吞噬黑暗。
这天,她来到了一条路上,准备吞噬这条路上的黑暗。
这条道路一共被分为n部分,每个部分上的黑暗数量为ai。
露米娅每次可以任取 连续的 未被吞噬过的 三部分,将其中的黑暗全部吞噬,并获得中间部分的饱食度。
露米娅想知道,自己能获得的饱食度最大值是多少?

输入描述:

第一行一个正整数n,代表道路被分的份数。
第二行有n个正整数ai,代表每一部分黑暗数量。
数据范围:3<=n<=10^5, 1<=ai<=10^9

输出描述:

一个正整数,代表最终饱食度的最大值。
示例1
输入
复制
7
2 4 1 4 2 1 8
输出
复制
6

说明

选择[2,4,1]和[4,2,1]这两段即可。饱食度为4+2=6。
示例2
输入
复制
7
2 4 1 7 2 1 8
输出
复制
7

说明

选择[1,7,2]这一段即可。饱食度为7。
值得注意的是,若取两段进行吞噬,反而最多只能获得6的饱食度,并不是最大的。

分析:

动态规划,f[i]=max(f[i-1],[i-3])

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const ll mod = 1e9+7;
int a[N];
ll f[N];
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	f[1]=f[2]=0;
	f[3]=a[2];
	for(int i=4;i<=n;i++)
	{
		f[i]=max(f[i-3]+a[i-1],f[i-1]);
	}
	printf("%lld",f[n]);
	return 0; 
} 

G .魔界伊始

链接:https://ac.nowcoder.com/acm/contest/8755/G
来源:牛客网

题目描述

神崎作为魔界之神,造人的方法是用沙子捏成人形,然后用魔法赋予其意识。
神崎有一台天平以及ai个砝码,另外她还有很多个同样重的烧杯和无穷多的沙子。平日她通过这些来称量出准确重量的沙子。
她想知道能否利用这些砝码称出重量为x的沙子?
一共有q次询问。
注:烧杯的数量可以视为无穷多个。烧杯的重量是未知的,但大小可以视为无穷大,即可以装任意多的沙子。烧杯中也可以放砝码。

输入描述:

第一行一个正整数n。
第二行有n个正整数,代表砝码的重量。
接下来的一行有一个正整数q。
接下来的q行,每一行有一个正整数x,分别代表一次询问。
数据范围:1≤q,n≤100000,1≤ai,x≤10^18

输出描述:

输出行。如果能撑出重量为的沙子,则输出"Yes"。否则输出"No"(不包含引号)。
示例1
输入
复制
2
6 20
2
8
7
输出
复制
Yes
No

说明

想要称出重量为8的沙子,可以先用两个砝码称出重量14的沙子,然后用这些沙子和一个重量6的砝码称出重量8的沙子。
但是无论如何,显然都不能称出重量7的沙子。

分析:
每两个数之间可以算得一个能称重的最小值,若两个数为a,b(a>=b),则c=a/b,最小值x=min(a-c*b,(c+1)*b-a),如果a%b==0,则最小值x=b;
只要是x的倍速的值都能称重

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const ll mod = 1e9+7;
ll a[N],b[N];
map<ll ,int> v;
int main()
{
	int n;
	scanf("%d",&n);
	ll t,x,y,z,w;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		if(i==1) t=a[i];
		else
		{
			if(a[i]>=t) x=a[i],y=t;
			else x=t,y=a[i];
			if(x%y==0) z=y;
			else
			{
				w=x/y;
				z=x-w*y;
				w++;
				z=min(z,w*y-x);
			}
			t=z;
		}
	}
	int q;
	scanf("%d",&q);
	while(q--)
	{
		scanf("%lld",&x);
		if(x%t==0) printf("Yes\n");
		else printf("No\n");
	}
	return 0; 
} 

H .芭芭拉冲鸭~

链接:https://ac.nowcoder.com/acm/contest/8755/H
来源:牛客网

题目描述

芭芭拉拿到了一棵无根树,树上每个节点被染成了红色或绿色或蓝色。(R=红色,G=绿色,B=蓝色)。
任意两点之间的距离均为1。
若一条边连接的两个节点的颜色不同,芭芭拉则可以在这条边上冲刺。
但是,芭芭拉在冲刺的时候不能掉头。例如芭芭拉已经从x冲到y了,就不能再回到。
现在芭芭拉想知道,自己在这棵树上最远能冲刺的距离是多少?

输入描述:

第一行一个正整数n,代表树的节点个数。
第二行是一个长度为n的字符串,仅由"R、G、B"这三种字符组成。第i个字符用于描述第i个节点的颜色。
接下来的n-1行,每行有两个正整数x和y,代表x和y节点有一条边连接。保证输入一定表示一棵树。

输出描述:

一个正整数,代表芭芭拉能冲刺的距离的最大值。
示例1
输入
复制
5
RRGBG
1 2
2 3
3 4
4 5
输出
复制
3

说明

这棵树是一条1-2-3-4-5的链,选择路径2-3-4-5即可。这样芭芭拉直接从2冲到5,冲刺距离为3。
注意1和2的颜色相同,所以芭芭拉并不能在1-2这条边上冲刺。
备注:
1<=n<=10^5

分析:

dfs从1个点开始搜,那么这个点所在的链上的长度最大值是以这个点为起点往下搜的长度最大值和次大值的和。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const ll mod = 1e9+7;
char a[N];
vector<int> q[N];
int ans=0,d[N];
void dfs(int u,int w)
{
	int x1=0,x2=0;
	for(int i=0;i<q[u].size();i++)
	{
		int v=q[u][i];
		if(v==w) continue;
		dfs(v,u);
		if(a[v]!=a[u])
		{
			if(d[v]+1>x1) x2=x1,x1=d[v]+1;
			else if(d[v]+1>x2) x2=d[v]+1;
			d[u]=max(d[u],d[v]+1);
		}
	}
	ans=max(ans,x1+x2);
}
int main()
{
	int n;
	scanf("%d",&n);
	getchar();
	scanf("%s",a+1);
	int x,y;
	for(int i=1;i<n;i++)
	{
		scanf("%d %d",&x,&y);
		q[x].push_back(y),q[y].push_back(x);
	}
	int k=0;
	dfs(1,0);
	printf("%d",ans);
	return 0; 
} 

J .永远亭的小游戏

链接:https://ac.nowcoder.com/acm/contest/8755/I
来源:牛客网

题目描述

蓬莱山辉夜由于整天宅在家,整个人都已经变成了一只废neet姬了。于是,她找来了铃仙和因幡帝,来玩一个小游戏。
辉夜拿出一个长度为n的数组a,铃仙拿出一个长度为m的数组b,因幡帝拿出一个长度为k的数组c。
她们等概率的在各自的数组中取一个数。辉夜想知道这三个取出的数的乘积的期望是多少?
可以证明,这个期望一定是有理数,可以写成x/y
的形式。请输出这个分数对10^9+7取模的值。
提示2:本题输入数据较大,推荐使用scanf或更快的输入方式

输入描述:

第一行输入三个正整数n,m,k,用空格隔开。分别代表辉夜、铃仙和因幡帝拿到的数组长度。
第二行输入n个正整数ai,代表辉夜拿到的数组。
第三行输入m个正整数bi,代表铃仙拿到的数组。
第四行输入k个正整数ci,代表因幡帝拿到的数组。
数据范围:1<=n,m,k<=10^6, 1<=ai,bi,ci<=10^6

输出描述:

最后乘积的期望对1e9+7取模的值。
示例1
输入
复制
2 2 2
1 2
1 1
2 4
输出
复制
500000008

说明

三个人各取一个数的组合有以下8种:
112=2
114=4
112=2
114=4
212=4
214=8
212=4
214=8
(2+4+2+4+4+8+4+8)/8=36/8=4.5
最终的乘积期望是4.5,即9/2,由于(500000008*2)%1000000007=9,因此输出500000008

分析:

根据说明可以知道答案的计算方法
先计算 x=c1+c2+…+ck
再计算 y=b1x+b2x+…bmx
然后计算 z=a1
y+a2y+…any
除数是p=nmk
运用逆元ans=z*pow(p,mod-2)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6+10;
const ll mod = 1e9+7;
map<ll,ll> q;
ll a[N],b[N],c[N];
ll fpow(ll x,ll y)
{
    ll ans=1,base=x%mod;
    while(y)
    {
        if(y&1) ans=(ans*base)%mod;
        base=(base*base)%mod;
        y>>=1;
    }
    return ans;
}
int main()
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for(int i=1;i<=m;i++) scanf("%lld",&b[i]);
    ll sum=0;
    for(int i=1;i<=k;i++)
    {
        scanf("%lld",&c[i]);
        sum=(sum+c[i])%mod;
    }
    ll y=0;
    for(int i=1;i<=m;i++)
    {
        y=(y+(b[i]*sum)%mod)%mod;
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ans=(ans+(a[i]*y)%mod)%mod;
    }
    ll q=n,e=m,r=k;
    y=q*e%mod*r%mod;
    ans=ans*fpow(y,mod-2)%mod;
    printf("%lld",ans);
    return 0;
}

K .玩具销售员

链接:https://ac.nowcoder.com/acm/contest/8755/K
来源:牛客网

题目描述

达达利亚是至冬国著名的玩具销售员。
有一天,他进货了n个玩具,但里面有m个次品。
每次检查他最多挑2个玩具出来进行检查。不过达达利亚慧眼如炬,他一眼就能看到次品的所在。
他想不超过次检查就把所有的次品挑出来,请问他是否可能做到这一点?

输入描述:

三个正整数n、m、k,用空格隔开。 1<=n,m,k<=100,m<=n

输出描述:

如果达达利亚能在k次检查中挑出所有的次品,输出"Yes"。
否则输出"No"。
(不要输出引号)
示例1
输入
复制
3 3 1
输出
复制
No

说明

一共3个玩具,全部是次品。达达利亚无论怎么挑选都无法在一次检查中成功挑完所有次品
示例2
输入
复制
5 3 2
输出
复制
Yes

说明

一共5个玩具,有3个次品。达达利亚第一次检查挑选一个次品和一个正品,第二次检查挑选两个次品,这样就挑完所有次品了。

分析:

如果m>k*2 ,答案是no

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
int main()
{
	int n,m,k;
	scanf("%d%d%d",&n,&m,&k);
	int x=k*2;
	if(x>=m) printf("Yes");
	else printf("No");
	return 0; 
} 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值