2022牛客寒假算法基础集训营2
比赛链接
2022牛客寒假算法基础集训营2
A.小沙的炉石
题目描述
小沙热衷于玩决斗法,今天他和他的弟弟玩起了炉石,弟弟特别特别的菜,但是为了照顾弟弟的自尊心,所以小沙想要恰好将弟弟斩杀。
恰好斩杀:弟弟的血量恰好变成\(0\)。
小沙当前的手上有\(n\)张法术进攻牌,每张牌都会消耗一点法力,造成一点基础伤害,有\(m\)张法术回复牌,不需要消耗法力值,每次可以恢复一点法力。小沙一开始有一点法力,法力没有上限。
他们都属于法术。
小沙场上有一个随从。他可以使你施法法术后使你的法术伤害\(+1\)。
每张法术进攻牌的伤害都等于法术伤害+基础伤害组成。
法术伤害初始为\(0\)。
你无法对该随从使用进攻法术牌。
随从也无法攻击。
现在小沙想问你,小沙现在能否恰好将弟弟斩杀。
输入描述:
第一行输入两个数\(1\leq n\leq 10^9,0\leq m\leq 10^9\)分别代表小沙手上的法术进攻牌和法术回复牌。
第二行输入一个\(1\leq k \leq 10^5\)代表小沙有\(k\)次询问
随后\(k\)行每行输入一个整数\(1\leq x\leq 10^{18}\)代表弟弟的血量
输出描述:
对于小沙的每一次询问,返回一行字符串
如果可以将弟弟斩杀输出“YES”(不带引号)
否则输出“NO”(不带引号)
输入
2 1
3
1
4
6
输出
YES
YES
NO
说明
如果弟弟的血量为\(1\)
小沙可以直接使用一张法术进攻牌伤害为\(1\)。恰好斩杀
如果弟弟的血量为\(4\)
小沙可以直接使用一张法术进攻牌伤害为\(1\),随后使用一张法术回复牌,然后使用一张法术进攻牌,总计造成\(1+0+3\)点伤害。恰好斩杀
如果弟弟的血量为\(6\)
则无法斩杀
解题思路
模拟
假设有 \(n\) 张进攻牌,\(m\) 张回复牌,则实际的进攻次数为 \(cnt=min(m+1,n)\),则最小的伤害为 \(1,3,\dots=cnt^2\),最大伤害为 \(m+1,m+2,\dots=cnt\times (m+1+m+cnt)/2\),现在要证明这些区间是连续的:
如果有 \(cnt\times (m+1+m+cnt)/2\geq (cnt+1)^2-1\) 则区间连续,其中 \(m+1\geq cnt\),则有 \(cnt\times (2\times cnt +cnt-1)\geq 2\times (cnt+1)^2-2\),解得 \(cnt\geq 5\),即 \(cnt\geq 5\) 时区间必然连续,对于 \(cnt<5\) 的情况,需要找出特例,这个挺难找的
- 时间复杂度:\(O(t)\)
代码
// Problem: 小沙的炉石
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/23477/A
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// %%%Skyqwq
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
int main()
{
int n,m,q;
scanf("%d%d",&n,&m);
int cnt=min(n,m+1);
LL mx=1ll*m*cnt+1ll*cnt*(cnt+1)/2;
scanf("%d",&q);
while(q--)
{
LL x;
scanf("%lld",&x);
if((cnt==2&&m==1&&x==3)||(cnt==3&&m==2&&x==8))
{
puts("NO");
continue;
}
if(x<=mx)puts("YES");
else
puts("NO");
}
return 0;
}
C.小沙的杀球
题目描述
小沙特别喜欢杀球(又菜又爱杀),加上小沙是个体能怪,所以他经常杀不死还喜欢杀,小沙只能杀到后场的高远球,如果是前场的小球则没有办法杀球,今天小沙和他的双打搭档一起训练杀球,我们假设小沙的体能一开始为\(X\),并且每次杀球体能都会消耗\(a\)点,每次非杀球都会恢复\(b\)点。现给出一段\(01\)序列,代表搭档打过来的球是小球还是高远球(\(0\)代表小球,\(1\)代表高远球),请问小沙这一场能最多杀多少个球。小沙的体能不能为负数。
输入描述:
第一行\(3\)个整数 \(0 \le x ,a,b \le 10^9\),每个整数用空格间隔
第二行一行\(01\)字符串(字符串长度不超过\(10^6\)) 代表搭档打过来的是小球还是高远球。
输出描述:
输出小沙的最多杀球次数
输入
10 2 1
111111
输出
5
说明
我们可以选择第\(2 3 4 5 6\)次杀球最多\(5\)次
解题思路
贪心
很容易想到一个贪心策略:能杀球则杀球。\(\color{red}{如何证明这个策略是正确的?}\)假设能杀球但是选择不杀球,即将这次杀球的体力留给后面杀球,这次选择恢复体力,这样显然会导致杀球的机会更少,如果选择杀球后面即使不能杀球也可以恢复体力达到相同的效果,并且这样杀球的机会更多
代码
// Problem: 小沙的杀球
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/23477/C
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// %%%Skyqwq
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
int a,b,res;
char s[1000005];
LL x;
int main()
{
scanf("%lld%d%d",&x,&a,&b);
scanf("%s",s+1);
for(int i=1;i<=strlen(s+1);i++)
if(s[i]=='1'&&x>=a)
res++,x-=a;
else
x+=b;
printf("%d",res);
return 0;
}
E.小沙的长路
题目描述
小沙有一个\(n\)个点的完全图(不知道定义可以点),你可以给每条边选择方向,规定每条边只能走一次,请问n个点的完全图的最长路径,现在现在小沙想要知道它的最小值和最大值分别是多少?
输入描述:
输入一个正整数\(n\leq 10^9\)
输出描述:
一行内输出\(n\)个点的完全图,他的最长路的最小值和最大值,两个数中间用空格间隔
输入
3
输出
2 3
说明
从图中可以看到\(3\)个节点时,最长路最短是\(2\),最大是\(3\)
解题思路
欧拉路径
对于最长路的最小值:
由于对于完全图而言,在有环情况下的最长路要比无环情况下的最长路要长,所以最长路的最小值一定是无环的,即只经过每个点一次,总长度为 \(n-1\)
对于最长度的最大值:
每条边只出现一次,即找欧拉路径,对于每个点度数为偶数的完全图而言,由于边也是连通的,其总是可以构造出一条欧拉回路,其总长度为 \(n\times (n-1)/2\),而对于度数为奇数的完全图而言,其可以转换为删除尽可能少的边,使其构造成一条欧拉路径,即可以保留两个奇数度数的点作为起点和终点,这两个点之间不用删边,其余 \(n-2\) 个点的度数需要减一使其为偶数,即在这 \(n-2\) 个点中删除 \((n-2)/2\) 条边,所以这种情况下的最终答案为 \(n\times (n-1)/2 - (n-2)/2\)
- 时间复杂度:\(O(1)\)
代码
// Problem: 小沙的长路
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/23477/E
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// %%%Skyqwq
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
int main()
{
int n;
cin>>n;
printf("%d %lld",n-1,1ll*n*(n-1)/2-1ll*(n%2==0)*(n-2)/2);
return 0;
}
F.小沙的算数
题目描述
小沙不会计数,所以他想从小学数学开始学起,今天老师布置了一大堆算数题,但爱耍小聪明的小沙发现这么多的算数题中,他们中间的符号都是\(+\)或者\(\times\) 并且每个题\(+\)和\(\times\)的位置都是一样的
小沙想要快点去玩耍,所以求助好哥哥你帮帮他快速的计算出答案。
由于答案数字过大 所以我们对\(1000000007\)取模
输入描述:
第一行给定二个数\(n\),代表有\(n\)个数进行计算,\(q\)代表有\(q\)次询问
\((2\leq n \leq 10^6,1\leq q \leq 10^5)\)
第二行给定一个长度为\(n-1\)的*+
字符串,表示我们要进行的计算符号是什么
第三行
给定\(n\)个整数,代表这每个位置上的数字
随后\(q\)行每行两个数字\(x,y\)
代表着将第\(x\)个数字改成\(y\)
且\(x\leq n\)
本题所有数均为正整数,且小于\(1000000007\)
但并不保证计算过程中是否会出现大于\(1000000007\)的值
输出描述:
对于每次查询,回答出整个算式的答案,每个答案占一行
(本题中的每次查询均不独立,也就是说每次查询修改之后的数,都会永远的修改)
输入
5 3
++*+
1 1 3 1 1
4 2
1 7
5 6
输出
9
15
20
解题思路
区间问题,模拟,哈希,逆元
不妨把一段乘法看成一个数,即类似于区间段数,预处理每段乘法的值及用哈希表或数组存下乘法的所有数字的位置对应的乘法段的下标,每次询问时,如果当前数位于乘法位置,则修改该数所在乘法的值,这里可用该处的乘法值除以修改前的值得到其他数的乘积,进而修改答案及该段乘法值;如果当前数位于加法位置,直接修改即可。另外还需要计算出询问前的表达式结果,这个可以模拟
- 时间复杂度:\(O(n)\)
代码
// Problem: 小沙的算数
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/23477/F
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// %%%Skyqwq
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int mod=1e9+7,N=1e6+5;
int n,q;
char s[N];
int m;
LL res,product[N],a[N];
unordered_map<int,int> mp;
int ksm(int a,int b,int p)
{
a=(a+p)%p;
int res=1%p;
for(;b;b>>=1)
{
if(b&1)
res=1ll*res*a%p;
a=1ll*a*a%p;
}
return res;
}
int main()
{
mp.clear();
scanf("%d%d",&n,&q);
scanf("%s",s+1);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
bool f=false;
m=0;
for(int i=1;i<=n-1;i++)
{
if(s[i]=='*')
{
if(!f)
{
m++;
product[m]=a[i]*a[i+1]%mod;
mp[i]=mp[i+1]=m;
}
else
product[m]=product[m]*a[i+1]%mod,mp[i+1]=m;
f=true;
}
else
f=false;
}
int t=1;
for(int i=1;i<=n-1;)
{
if(i==1)
{
if(s[1]=='*')
{
while(i<=n-1&&s[i]=='*')i++;
res=product[t++];
}
else
{
if(i+1<=n-1)
{
if(s[i+1]=='+')res=(a[1]+a[2])%mod,i++;
else
{
res=a[1];
i++;
while(i<=n-1&&s[i]=='*')i++;
res=(res+product[t++])%mod;
}
}
else
{
res=(a[1]+a[2])%mod;
i++;
}
}
}
else
{
if(i+1<=n-1)
{
if(s[i+1]=='*')
{
res=(res+product[t++])%mod;
i++;
while(i<=n-1&&s[i]=='*')i++;
}
else
res=(res+a[i+1])%mod,i++;
}
else
res=(res+a[i+1])%mod,i++;
}
}
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
int lst=a[x];
a[x]=y;
int bl=mp[x];
if(bl)
{
res=(res+(y-lst+mod)*product[bl]%mod*ksm(lst,mod-2,mod)%mod)%mod;
product[bl]=(product[bl]+(y-lst+mod)*product[bl]%mod*ksm(lst,mod-2,mod)%mod)%mod;
}
else
{
res=(res+y-lst+mod)%mod;
}
printf("%lld\n",res);
}
return 0;
}
H.小沙的数数
题目描述
有一个\(a\)数组,我们已知他的长度为\(n\),\(a[+]\)的和为\(m\),请问如果我们想要\(a[⊕]\)的值最大,数组\(a\)在满足\(a[+]=m\)时有多少种情况
我们定义\(a[+]\)指\(a_1+a_2....a_n\)的值
a[⊕]指\(a_1⊕a_2⊕a_3....a_n\) 的值
其中\(a\)数组全部都为非负整数。
答案对\(1e9+7\)取模
输入描述:
输入两个整数\(1\leq n \leq 10^{18},0\leq m \leq 10^{18}\)
输出描述:
输出一个整数表示方案数
输入
3 1
输出
3
说明
原数组可以为
0 0 1
0 1 0
1 0 0
这三种可能
解题思路
数学
结论:\(a+b \geq a \bigoplus b\),异或为不进位的加法
由结论:\(a[\bigoplus ]=a[+]=m\),由于异或值的固定的,进位会改变固定的异或值,所以不允许进位,另外还有一种理解:有数学公式:\(a+b=a\& b*2+a \bigoplus b\),要使 \(a \bigoplus b\) 最大,即 \(a\& b=0\),即每一位两两之间都要有 \(0\),即二进制下每一位最多只能出现一个 \(1\),且该位由 \(m\) 决定。另外,每一位的 \(1\) 的位置是互不影响的,且 \(1\) 的位置可以出现在数组中任意一个数的对应位上,即每一位的 \(1\) 由 \(n\) 种情况
- 时间复杂度:\(O(logm)\)
代码
// Problem: 小沙的数数
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/23477/H
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// %%%Skyqwq
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int mod=1e9+7;
LL n,m;
int main()
{
scanf("%lld%lld",&n,&m);
n%=mod;
int res=1;
for(;m;m-=m&-m)res=1ll*res*n%mod;
printf("%d",res);
return 0;
}
I.小沙的构造
题目描述
小沙的构造题没了,他很伤心,所以他想把这个构造送给你们,而你需要的就是彪起你的手速,抓紧抢到这题的一血。
这题小沙想让你构造出一串字符串,这个字符串有以下几个特点。
- 他是对称串,他关于这个字符串的垂直对称。例如"()",我们将他翻转过来他也是“()”,例如“p“翻转过来就是“q“。
- 这个串串的所有字符都是除小写字母以外的可见字符(不包括空格)。
现在小沙想让你构造一个长度为\(n\)的不同字符数量为\(m\)的一个字符串。
可见字符如下:
!"#$%&'()*+,-./0123456789:;<=>?@[\]^_`QWERTYUIOPASDFGHJKLZXCVBNM{}|~
其中具有对称性质的有
"!'*+-.08:=^_WTYUIOAHXVM|<>\/[]{}()
输入描述:
第一行输入两个整数\(1\leq n\leq 10^4 ,1\leq m\leq 36\)。
输出描述:
如果可以构造出这样的一个字符串,便输出这个字符串
否则输出\(-1\)
示例1
输入
3 1
输出
OOO
示例2
输入
3 3
输出
<=>
解题思路
构造
如果个数为奇数,先处理中间的字符,再优先处理括号之类的对称字符,然后再处理单个对称的字符,再根据剩余的不同个数是否为零判断是否存在构造方案,最后根据第一个填充的字符填充剩余的字符
- 时间复杂度:\(O(n)\)
代码
// Problem: 小沙的构造
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/23477/I
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
// %%%Skyqwq
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
int n,m;
string s="<\\[{(!'*+-.08:=^_WTYUIOAHXVM|\"",t=">/]})!'*+-.08:=^_WTYUIOAHXVM|\"";
int main()
{
scanf("%d%d",&n,&m);
bool success=true;
vector<char> res(n);
if(n&1)
{
res[n/2]=s[5];
m--;
}
bool f=false;
int rest=m,pos=0;
while(rest-2>=0&&pos<5&&pos<n/2)f=true,res[pos]=s[pos],res[n-1-pos]=t[pos],pos++,rest-=2;
int pos1=n&1?6:5;
while(rest&&pos<n/2&&pos1<30)res[pos]=s[pos1],res[n-1-pos]=t[pos1],pos++,pos1++,rest--;
if(rest)success=false;
for(int i=pos;i<n/2;i++)
{
if(f)res[i]=s[0],res[n-1-i]=t[0];
else
res[i]=res[n-1-i]=s[5];
}
if(success)
{
for(char c:res)putchar(c);
}
else
puts("-1");
return 0;
}