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代码:


推广一波小飞龙博客:戳这里@不会飞的小飞龙

posted @ 2022-01-26 18:27  不会飞的小飞龙  阅读(93)  评论(0编辑  收藏  举报
Live2D