2022牛客寒假算法基础集训营2 个人题解
2022牛客寒假算法基础集训营2 个人题解
比赛链接:2022牛客寒假算法基础集训营2
A题 小沙的炉石
题目大意:
给出 \(n\) 张攻击牌和 \(m\) 张回蓝牌,初始法力为 \(1\) ,打出攻击牌需要消耗一点蓝量,并造成1+当前法术加成点伤害,打出回蓝牌增加一点法力,每打出一张牌,法术伤害增加一点,初始为 \(0\) ,对于每次询问x,输出能否恰好杀死
思路解析:
首先因为蓝量的限制,我们最多能够打出的攻击牌最多有 \(min(n,m+1)\) 张,显然对于任意攻击牌数来说,它能够造成的伤害是连续的一段区间值,最小值那就是 \(10101...\) ,最大值就是 \(000...111\) ( \(0\) 表示回蓝牌, \(1\) 表示攻击牌),我们就可以计算出对于每个攻击牌数的斩杀范围,只需要二分判断即可,另外我们发现,攻击下限为 \(x^2\) ,所以我们可以直接通过开根号来找到最近的下限,然后判断是否在区间上限以下就可以
AC代码:
#include<bits/stdc++.h>
#include <cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int maxn=1e6+5;
ll n,m,q;
int main(){
IOS
cin>>n>>m>>q;
n=min(n,m+1);
while(q--){
ll x;
cin>>x;
ll now=sqrt(x);
now=min(now,n);
if(x<=(2*m*now+now*now+now)/2)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
B题 小沙的魔法
题目大意:
思路解析:
AC代码:
C题 小沙的杀球
题目大意:
初始体力为 \(x\) ,每次杀球消耗 \(a\) 体力,不杀增长 \(b\) 体力,问最多杀几次
思路解析:
一句话:能杀就杀,模拟即可
AC代码:
#include<bits/stdc++.h>
#include <cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int maxn=1e6+5;
ll x,a,b;
string s;
int main(){
IOS
cin>>x>>a>>b;
cin>>s;
ll ans=0;
for(int i=0;i<s.size();i++){
if(s[i]=='0'){
x+=b;
}else {
if(x>=a){
x-=a;
ans++;
}
else x+=b;
}
}
cout<<ans<<endl;
}
D题 小沙的涂色
题目大意:
思路解析:
AC代码:
E题 小沙的长路
题目大意:
\(n\) 个点的完全图中,每条边的方向你可以随意选择,但每条边只能走一次,问最长路的最大值和最小值
思路解析:
首先我们考虑最小值,小飞龙是这么想的:
我们可以一个点一个点得往外拿,并且让与这个点相连的边都指向这个点,然后去掉这个点,以及和他相连的边,最后只会剩下一个点。我们会发现,每一次拿点就相当于走一条必经之路,那么一共拿走 \(n-1\) 个点,那么最长路的值就是 \(n-1\) 。
然后我们来考虑最大值的情况:
-
如果 \(n\) 为奇数,那么每个点的度为 \(n-1\) 为偶数,所以形成了一个欧拉回路,那么所有的边可以走到,最大值为 \(n*(n-1)/2\)
-
如果 \(n\) 为偶数,那么我们可以把问题转化为至少删多少条边会使剩下的为欧拉回路,所以我们至少要删除 \((n-2)/2\) 条边才能使剩下的边都能走到
AC代码:
#include<bits/stdc++.h>
#include <cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int maxn=1e6+5;
int main(){
IOS
ll n;
cin>>n;
ll x,y;
if(n%2){
x=(n-1);y=n*(n-1)/2;
}else {
x=(n-1);y=n*(n-1)/2-(n-2)/2;
}
cout<<min(x,y)<<" "<<max(x,y)<<endl;
}
F题 小沙的算数
题目大意:
给出一个只有 \(+*\) 的算式,给出每个位置的数字以及数字之间的符号,对于每个询问 \(x,y\) ,输出把第 \(x\) 个数换成 \(y\) 之后算式的结果(询问不独立,修改代表永久修改)
思路解析:
对于未修改前的算式我们是可以求出他的最终结果的,然后我们把每一个连乘看做一个块,给每个块打上标记,计算每一个快的值,那么最终结果就是所有块的值求和。
对于一次修改,我们把他看做块上的操作,相当于把块的结果先除一个 \(a[x]\) ,再乘上一个 \(y\) ,替换掉 \(a[x]\) ,然后最终的答案只需要加上过程所产生的差值即可(注意除的时候处理逆元)
AC代码:
#include<bits/stdc++.h>
#include <cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int maxn=1e6+5;
const int mod=1e9+7;
int n,q;
ll a[maxn];
ll ans;
int id[maxn];
ll sum[maxn];
ll ksm(ll a, ll b)
{
ll ans=1,base=a%mod;
while(b)
{
if(b&1)
ans=ans*base%mod;
base=base*base%mod;
b>>=1;
}
return ans;
}
ll inv(ll x){
return ksm(x,mod-2)%mod;
}
int main(){
IOS
cin>>n>>q;
string s;
cin>>s;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]%=mod;
}
ll now=a[1];
id[1]=1;
for(int i=2;i<=n;i++){
if(s[i-2]=='+'){
id[i]=id[i-1]+1;
ans+=now%mod;
now=a[i]%mod;
ans%=mod;
}else{
id[i]=id[i-1];
now*=a[i]%mod;
now%=mod;
}
}
ans+=now%mod;
for(int i=1;i<=n;i++)sum[i]=1;
for(int i=1;i<=n;i++){
sum[id[i]]*=a[i]%mod;
sum[id[i]]%=mod;
}
while(q--){
ll x,y;
cin>>x>>y;
y%=mod;
ll last=sum[id[x]]%mod;
sum[id[x]]*=inv(a[x])%mod;
sum[id[x]]%=mod;
sum[id[x]]*=y%mod;
sum[id[x]]%=mod;
ans=(ans%mod+sum[id[x]]%mod-last+100ll*mod)%mod;
cout<<ans%mod<<endl;
a[x]=y;
}
}
G题 小沙的身法
题目大意:
给出一颗树,和每个节点的高度,每一次询问 \(u\) 到 \(v\) 的花费
花费定义为从高度低的节点跳到高度高的节点的高度差,只能跳到临近节点
思路解析:
我们可以用前缀和的思想求出从根节点到节点u的花费 \(sum[]\) 和逆向花费 \(resum[]\) ,对于每一个查询 \(x,y\) ,答案即为 \(lca(x,y)\) 到 \(x\) 的花费加上, \(lca(x,y)\) 到 \(y\) 的花费,即 \(a[x](初始高度)+resum[x]-resum[p]+sum[y]-sum[p]\)
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
int n,m,r,mod,s;
int e[maxn*2],nex[maxn*2],h[maxn],id;
int dfn[maxn],sz[maxn],fa[maxn],dep[maxn],son[maxn],top[maxn];
void add(int x,int y){
e[++id]=y;
nex[id]=h[x];
h[x]=id;
}
ll sum[maxn],resum[maxn];
int a[maxn];
void dfs1(int u,int f){
fa[u]=f;
dep[u]=dep[f]+1;
sz[u]=1;
int maxsz=-1;
if(f!=u){
sum[u]=sum[f];
resum[u]=resum[f];
if(a[u]>a[f])sum[u]+=a[u]-a[f];
else resum[u]+=a[f]-a[u];
}
for(int i=h[u];i;i=nex[i]){
int j=e[i];
if(j==f)continue;
dfs1(j,u);
sz[u]+=sz[j];
if(sz[j]>maxsz){
maxsz=sz[j];
son[u]=j;
}
}
}
void dfs2(int u,int t){
top[u]=t;
if(!son[u])return ;
dfs2(son[u],t);
for(int i=h[u];i;i=nex[i]){
int j=e[i];
if(j==fa[u]||j==son[u])continue;
dfs2(j,j);
}
}
int lca(int u,int v)
{
while(top[u]!=top[v]) {
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]? u:v;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
s=1;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
add(x,y);
add(y,x);
}
dfs1(s,s);
dfs2(s,s);
for(int i=1;i<=n;i++){
//cout<<resum[i]<<endl;
}
while(m--){
int x,y;
cin>>x>>y;
int p=lca(x,y);
cout<<a[x]+resum[x]-resum[p]+sum[y]-sum[p]<<endl;
}
}
H题 小沙的数数
题目大意:
已知长度为 \(n\) 的序列的和为 \(m\) ,求出序列异或值最大的时候有多少种情况。
思路解析:
求出 \(m\) 二进制中 \(1\) 的数量 \(x\) , \(n^x\) 即为答案
AC代码:
#include<bits/stdc++.h>
#include <cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int maxn=1e6+5;
const int mod=1e9+7;
ll f[maxn];
ll ksm(ll a, ll b)
{
ll ans=1,base=a%mod;
while(b)
{
if(b&1)
ans=ans*base%mod;
base=base*base%mod;
b>>=1;
}
return ans;
}
int main(){
IOS
ll n,m;
cin>>n>>m;
ll tot=0;
while(m){
tot+=m%2;
m/=2;
}
cout<<ksm(n,tot)%mod<<endl;
}
I题 小沙的构造
题目大意:
给出 \(35\) 种对称的字符,然你构造一个长度为 \(n\) ,包含 \(m\) 种不同字符的回文串,无解输出 \(-1\)
思路解析:
注意到 \(35\) 种中有 \(5\) 对是成对出现的,模拟即可
(小飞龙觉得有一点毒瘤,因为小飞龙还是太菜了QAQ)
附上赛中写得巨丑无比的代码QAQ
AC代码:
#include<bits/stdc++.h>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int maxn=1e6+5;
int a[maxn]={0,34,33,39,42,43,45,46,48,56,58,61,94,95,87,84,89,85,73,79,65,72,88,86,77,124};
char x[50]={'0','{','[','<','(','\\'};
char y[50]={'0','}',']','>',')','/'};
deque<char>q;
int main(){
IOS
int n,m;
cin>>n>>m;
int top=n;
if(m==1){
for(int i=1;i<=n;i++){
cout<<"0";
}
return 0;
}
if(m==2){
if(n%2){
q.push_back('0');
for(int i=1;i<=n/2;i++){
q.push_front(a[1]);
q.push_back(a[1]);
}
while(q.size()){
cout<<q.front();
q.pop_front();
}
}else{
for(int i=1;i<=n/2;i++){
q.push_front(x[1]);
q.push_back(y[1]);
}
while(q.size()){
cout<<q.front();
q.pop_front();
}
}
return 0;
}
if(m>35||n<m){
cout<<-1<<endl;
return 0;
}
if(n%2==m%2){
int ok=0;
if(n%2){
q.push_front('+');
m--;
n--;
ok=1;
}
for(int i=1;i<=min(5,m/2);i++){
q.push_front(x[i]);
q.push_back(y[i]);
}
m-=min(5,m/2)*2;
n-=min(5,m/2)*2;
if(q.size()>top){
cout<<-1<<endl;
return 0;
}
int bs=0;
for(int i=1;i<=m+bs;i++){
char now=a[i];
if(ok&&now=='+'){
bs++;
continue;
}
q.push_front(now);
q.push_back(now);
}
if(q.size()>top){
cout<<-1<<endl;
return 0;
}
int sz=q.size();
while(q.size()!=top){
q.push_front(x[1]);
q.push_back(y[1]);
}
while(q.size()){
cout<<q.front();
q.pop_front();
}
}
else {
n-=2;m--;
int ok=0;
if(n%2){
q.push_front('+');
m--;
n--;
ok=1;
}
q.push_front('=');
q.push_back('=');
for(int i=1;i<=min(5,m/2);i++){
q.push_front(x[i]);
q.push_back(y[i]);
}
m-=min(5,m/2)*2;
n-=min(5,m/2)*2;
if(q.size()>top){
cout<<-1<<endl;
return 0;
}
int bs=0;
for(int i=1;i<=m+bs;i++){
char now=a[i];
if(ok&&now=='+'){
bs++;
continue;
}
if(now=='='){
bs++;
continue;
}
q.push_front(now);
q.push_back(now);
}
if(q.size()>top){
cout<<-1<<endl;
return 0;
}
int sz=q.size();
while(q.size()!=top){
q.push_front(x[1]);
q.push_back(y[1]);
}
while(q.size()){
cout<<q.front();
q.pop_front();
}
}
}
J题 小沙的Dota
题目大意:
思路解析:
AC代码:
K题 小沙的步伐
题目大意:
有 \(9\) 个接球点,\(5\) 是中心,每次接球回到中心,给出接球序列,问每个点经过的次数
思路解析:
计数即可,\(5\) 单独处理
AC代码:
#include<bits/stdc++.h>
#include <cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int maxn=1e6+5;
string s;
int a[maxn];
int main(){
IOS
cin>>s;
int tot=0;
for(int i=0;i<s.size();i++){
if(s[i]=='5')continue;
a[s[i]-'0']++;
tot++;
}
for(int i=1;i<=9;i++){
if(i==5)cout<<tot<<" ";
else cout<<a[i]<<" ";
}
}
L题 小沙的remake(普通版)
题目大意:
思路解析:
AC代码:
M题 小沙的remake(竞速版)
题目大意:
思路解析:
AC代码:
推广一波小飞龙博客:戳这里@不会飞的小飞龙
本文来自博客园,作者:不会飞的小飞龙,转载请注明原文链接:https://www.cnblogs.com/xiaofeilong7816/p/15838401.html