2022牛客寒假算法基础集训营4 个人题解

2022牛客寒假算法基础集训营4 个人题解

比赛链接:2022牛客寒假算法基础集训营4

A题 R

题目大意:

小红拿到了一个长度为 \(n\) 的字符串,该字符串仅由大写字母组成。
小红很喜欢红色(用\(R\)字母表示),但她非常讨厌紫色(用\(P\)字母表示)。
她想取一个连续子串,该子串包含至少 \(k\)\(R\)字符,且不能包含\(P\)字符。
你能告诉她有多少合法的方案可以取到吗?
注:只要连续子串的起始位置或终止位置不同,我们就认为是两个不同的方案。

思路解析:

双指针

AC代码:

#include <algorithm>
#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;

char s[maxn];
int sum[maxn];
int a[maxn];

int main(){

    IOS

    int n,k;
    cin>>n>>k;

    cin>>s+1;

    int top=n+1;

    for(int i=1;i<=n;i++){
        if(s[i]=='P')sum[i]=sum[i-1]+1;
        else sum[i]=sum[i-1];

        int now=n-i+1;
        if(s[now]!='P')a[now]=top;
        else top=now;
    }

    

    int tp=1,sum=0;
    ll ans=0;

    for(int i=1;i<=n;i++){
        if(s[i]=='P'){
            sum=0;
            tp=i+1;
        }
        if(s[i]=='R'){
            sum++;
            while(sum>=k&&tp<i+1){
                ans+=abs(i-a[i]);
                sum-=(s[tp]==s[i]);
                tp++;
            }
        }
    }

    while(tp<n+1){
        tp++;
        if(s[tp]=='R')tp+=1,sum-=1;
        if(sum>=k)ans+=1;
    }

    cout<<ans<<endl;
}

B题 进制

题目大意:链接:https://ac.nowcoder.com/acm/contest/23479/B

来源:牛客网

小红拿到了一个长度为 \(n\) 的数字串 \(s\)(只有 \('0' ~ '9'\) 这十种字符),她有 \(q\) 次以下两种操作:

第一种: 输入\(1 x y\),修改第 \(x\) 个字符为 \(y\) ,即令\(s_x=y\)

第二种: 输出 \(2 x y\) ,代表查询区间 \([x,y]\),该区间子串所能表示的某进制的最小值(进制必须合法,且必须是二进制到十进制之间,可以包含前导零),对 \(1000000007\) 取模。

思路解析:

十颗线段树,对每一种进制建树维护

(贴一份队友聚聚的代码)AC代码:

#include <bits/stdc++.h>
#define debug(x) cout << #x << "=" << x << '\n';
#define ls u << 1
#define rs u << 1 | 1
#define ll long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e5 + 10, mod = 1000000007;

int n, q;
char s[N];

ll qmi(ll a, ll b) {
	if (!a) return 0; 
	ll res = 1;
	while(b) {
		if(b & 1) res = res * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return res % mod;
}

struct segment_tree {
	struct node {
		int l, r;
		int v, maxv;
		ll val;
		int len, jz;
	} tr[N * 4];

	void pushup(node &u, node &l, node &r) {
		int t = l.jz;
		u.jz = l.jz;
		u.len = l.len + r.len;
		u.maxv = max(l.maxv, r.maxv);
		u.val = (l.val * qmi(t, r.len) % mod + r.val) % mod;
	}

	void pushup(int u) { pushup(tr[u], tr[ls], tr[rs]); }

	void build(int u, int l, int r, int k) {
		if(l == r)
			tr[u] = {l, r, s[l] - '0', s[l] - '0', s[l] - '0', 1, k};
		else {
			tr[u] = {l, r};
			int mid = l + r >> 1;
			build(ls, l, mid, k), build(rs, mid + 1, r, k);
			pushup(u);
		}
	}

	void modify(int u, int x, int v) {
		if(tr[u].l == x && tr[u].r == x) {
			int k = tr[u].jz;
			tr[u] = {x, x, v, v, v, 1, k};
		} else {
			int mid = tr[u].l + tr[u].r >> 1;
			if(x <= mid)
				modify(ls, x, v);
			else
				modify(rs, x, v);
			pushup(u);
		}
	}

	node query(int u, int l, int r) {
		if(tr[u].l >= l && tr[u].r <= r) return tr[u];
		int mid = tr[u].l + tr[u].r >> 1;
		if(r <= mid) return query(ls, l, r);
		if(l > mid) return query(rs, l, r);
		node res, left = query(ls, l, r), right = query(rs, l, r);
		pushup(res, left, right);
		return res;
	}

} tree[11];

void solve() {
	cin >> n >> q;
	cin >> (s + 1);
	for(int i = 2; i <= 10; i++) { tree[i].build(1, 1, n, i); }

	while(q--) {
		int op, x, y;
		cin >> op >> x >> y;
		if(op == 1) {
			for(int i = 2; i <= 10; i++) tree[i].modify(1, x, y);
		} else {
			auto tmp = tree[2].query(1, x, y);
			int mx = tmp.maxv;
			cout << tree[mx + 1].query(1, x, y).val << '\n';
		}
	}
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	// int T;cin>>T;while(T--)
	solve();
}

C题 蓝彗星

题目大意:

给出 \(n\) 颗彗星以及他们的颜色,蓝或者红,持续时间和开始时间,问有多少秒只能看到蓝彗星看不到红彗星

思路解析:

蓝红分别差分即可

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 a[maxn];
char col[maxn];
int b[maxn],r[maxn];

int main(){

    IOS

    int n,t;
    cin>>n>>t;

    cin>>col+1;

    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(col[i]=='B')b[a[i]]++,b[a[i]+t]--;
        else r[a[i]]++,r[a[i]+t]--;
    }

    int x=0,y=0;

    ll ans=0;

    for(int i=1;i<=200005;i++){
        x+=b[i];
        y+=r[i];
        if(x&&y==0)ans++;
    }

    cout<<ans<<endl;

}

D题 雪色光晕

题目大意:

二维坐标中,给出小红和小果的坐标,每一秒给出一个方向向量,并且小红按照它移动,问在移动过程中,两个人的最短距离是多少

思路解析:

基础二维几何,点到线段的距离

AC代码:

//代码太长我就不放了hh

E题 真假签到题

题目大意:

给出以下代码:

long long f(long long x){
    if(x==1)return 1;
    return f(x/2)+f(x/2+x%2);
}

给出\(x\),求\(f(x)\)

思路解析:

通过观察,我们发现\(f(x)=x\),输出\(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;

long long f(long long x){
    if(x==1)return 1;
    return f(x/2)+f(x/2+x%2);
}

int main(){

    IOS

    ll n;
    cin>>n;

    cout<<n<<endl;

}

F题 小红的记谱法

题目大意:

给出两种记谱方式,给出第二种,写出其对应的第二种(是不是觉得小飞龙没有讲清楚题意?主要是题面太长难解释自行观看hh)

思路解析:

计数,模拟即可

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;

map<char ,int>q;

int main(){

    IOS

    string s;
    cin>>s;

    int x=0,y=0;
    q['C']=1;
    q['D']=2;
    q['E']=3;
    q['F']=4;
    q['G']=5;
    q['A']=6;
    q['B']=7;

    for(int i=0;i<s.size();i++){
        if(s[i]=='<')x++;
        else if(s[i]=='>')y++;
        else {
            int now=q[s[i]];
            cout<<now;
            if(x>y){
                for(int i=y;i<x;i++)cout<<".";
            }
            else if(y>x)for(int i=x;i<y;i++)cout<<"*";
        }

    }

}

G题 子序列权值乘积

题目大意:

求出数组中非空子序列的权值的乘积是多少

权值定义为:\(最大值*最小值\)

思路解析:

我们发现序列顺序对于序列的权值是没有影响的,我们可以先排序与,枚举每个值作为所选序列的最大值/最小值,分别计算其对于答案的贡献。

作为最小值的贡献:\(a[i]^{2^{i-1}}\)
作为最大值的贡献:\(a[i]^{2^{n-i}}\)

由于指数较大,所以使用欧拉降幂

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 a[maxn];
ll ans=1;

ll ksm(ll a,ll b,ll mod){
    ll ans=1,base=a%mod;
    while(b){
        if(b&1)
            ans=ans*base%mod;
        base=base*base%mod;
        b>>=1;
    }
    return ans%mod;
}

ll oljm(ll m,ll k,ll n){
    if(m%mod==0)return 0;
    ll p=ksm(k,n,mod-1);
    return ksm(m,p,mod)%mod;
}

int main(){

    IOS

    int n;
    cin>>n;

    for(int i=1;i<=n;i++)cin>>a[i];

    sort(a+1,a+n+1);

    for(int i=1;i<=n;i++){
        ans*=oljm(a[i],2,i-1);
        ans%=mod;
        ans*=oljm(a[i],2,n-i);
        ans%=mod;
    }

    cout<<ans%mod<<endl;

}

H题 真真真真真签到题

题目大意:

小红和紫被困在一个正方体的内部。紫先选择了一个位置,然后小红选择一个位置。紫希望离小红尽可能近,小红希望离紫尽可能远。两人都会选择最优策略。
已知她们最终的距离为 \(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;



int main(){


    double x;
    cin>>x;

    x=x*2/sqrt(3);

    printf("%0.6f",x*x*x);

}

I题 爆炸的符卡洋洋洒洒

题目大意:

小红有\(n\)张卡,每张卡消耗为\(a_i\),威力为\(b_i\),问能选择最大的威力和是多少

特别的,必须保证所选卡的消耗和为\(k\)的倍数

思路解析:

\(01\)背包的变形版本

我们只需要把转移改为\(f[i][j]=max(f[i][j],f[i-1][(j-v[i]+m)\%m]+w[i])\)

特别的我们需要判断是否有方案可以转移才能够向下转移

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=1005;

ll f[maxn][maxn];
ll v[maxn], w[maxn];
int n, m;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> v[i] >> w[i];
        v[i]%=m;
        f[i][v[i]]=w[i];
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < m; j++) {
            f[i][j] = max(f[i][j],f[i-1][j]);
            if(f[i - 1][(j - v[i]+m)%m])
            f[i][j] = max(f[i][j], f[i - 1][(j - v[i]+m)%m] + w[i]);
        }
    }

    if(f[n][0]==0)cout<<-1<<endl;
    else cout<<f[n][0]<<endl;
}

J题 区间合数的最小公倍数

题目大意:

\([l,r]\) 区间所有合数的最小公倍数是多少

思路解析:

将区间内每个数分解质因数,那么每一个质因子取最大值即为乘积的质因子,然后计算即可

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 st[maxn],prime[maxn],tot;

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;
}

void get_primes(int n){
    for(int i=2;i<=n;i++){
        if(!st[i])prime[++tot]=i;
        for(int j=1;prime[j]<=n/i;j++){
            st[prime[j]*i]=1;
            if(i%prime[j]==0)break;
        }
    }
}

int p[maxn];

void divide(int x)
{
    for (int i = 2; i <= x / i; i ++ )
        if (x % i == 0)
        {
            int s = 0;
            while (x % i == 0) x /= i, s ++ ;
            p[i]=max(p[i],s);
            //cout << i << ' ' << s << endl;
        }
    if (x > 1) p[x]=max(p[x],1);
}


int main(){

    IOS

    get_primes(30505);

    int l,r;
    cin>>l>>r;

    if(l==1)l++;

    ll ans=1;
    int sum=0;

    for(int i=l;i<=r;i++){
        if(st[i]){
            divide(i);
            sum++;
        }
    }

    if(sum==0)cout<<-1<<endl;
    else {
        for(int i=1;i<=tot;i++){
            ans*=ksm(prime[i],p[prime[i]])%mod;
            ans%=mod;
        }
        cout<<ans<<endl;
    }

}

K题 小红的真真假假签到题题

题目大意:

小红拿到了一个正整数 \(x\) 。她想构造一个正整数 \(y\),满足以下性质:

  • \(y\)\(x\) 的倍数,且 \(x\)\(y\) 不能相等。
  • \(x\) 在二进制表示下(为一个\(01\)串)是 \(y\) 的二进制表示的一个子串。且 \(x\)\(y\) 的二进制表示的\(1\)的个数不能相同。
  • \(y\) 必须为不超过 \(10^{19}\)的正整数。

思路解析:

很多种构造方式,小飞龙的方法:

\(110 -> 110110\)
\(1011 -> 10111011\)
\(10 -> 1010\)

这样的方式扩大

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 x;
    cin>>x;

    ll op=x;

    ll now=x;
    int sum=0;

    while(now){
        now/=2;
        sum++;
    }

    x<<=sum;
    x+=op;

    cout<<x<<endl;
}

L题 在这冷漠的世界里光光哭哭

题目大意:

思路解析:

AC代码:


https://files.cnblogs.com/files/blogs/696109/tt.rar?t=1691327061&download=true

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

posted @ 2022-02-10 10:15  不会飞的小飞龙  阅读(353)  评论(0编辑  收藏  举报
Live2D