HBCPC2024 河北省大学生程序设计竞赛 补题

 原题地址 :Dashboard - The 8th Hebei Collegiate Programming Contest - Codeforces

目录

A.Update

C.GooseGooseDuck

E.Breakfast II

G.Bracelet

I.Subnt

J. Iris’Food

K.Welcom

A.Update

题目大意:选择一种字母x,将字符串中的所有该x都替换成另一种字符y,问把所有字符都替换成字符 i 的最小操作次数。

思路:直接枚举字符串,记录不为 i 的字符有多少种即可。

#include <bits/stdc++.h>
using namespace std;

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    
    string s; cin>>s;
    map<char,int> mp;

    int ans=0;
    for(int i=0;i<s.size();i++)
    {
        if(!mp[s[i]]&&s[i]!='i') ans++;
        mp[s[i]]++;
    }
    cout<<ans<<endl;
    return 0; 
}

 

C.GooseGooseDuck

题目大意: 有n个人玩鹅鸭杀,每个人有一个接受范围,当当前参加人数在这个人的接受范围类的话,这个人就愿意参加,问该如何给这n个人排序

思路:可以固定左边界,按照左边界来枚举,这样的话一定是右边界越靠近越好,右边界的最小值可以用优先队列来维护,来找到最小的大于右边界的值即可。

#include <bits/stdc++.h>
#define ll long long 
#define fi first
#define se second
#define pii pair<int,int> 
using namespace std;
const int N =1e6+10;
vector<pair<int,int>>e[N];

int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);

    int n; cin>>n;
    priority_queue<pii,vector<pii>,greater<pii>>q;
    for(int i=1;i<=n;i++)
    {
        int l,r; cin>>l>>r;
        e[l].push_back({r,i});
    }
    vector<int> ans;
    for(int i=1;i<=n;i++)
    {
        for(auto it:e[i-1]) q.emplace(it);
        while(!q.empty()&&q.top().fi<i-1) q.pop();
        if(q.empty()) break;
        ans.push_back(q.top().se);
        q.pop(); 
    }
    cout<<ans.size()<<endl;
    for(auto x:ans) cout<<x<<' ';
    return 0; 
}

E.Breakfast II

题目大意:有k个学生,需要购买n个包子和m个鸡蛋,每个学生可以选择不去,或着去多个食堂购买,购买东西的学生最后还需要去办公室。每个食堂限制了只能购买b个包子和e个鸡蛋,给出了食堂,办公室和学生的坐标,求出k个学生购买n个包子和m个鸡蛋的最短距离之和。

思路:由于每个学生会有多种选择,他可以选择不去,和可以选择去一个食堂,两个食堂,三个食堂。每个学生相互独立,每个学生的选择为四种情况中只能选择一种,由此看出是一个很经典的分组背包问题。

学生们需要的最小购买次数就为max(⌈n/b​⌉,⌈m/e​⌉),对应是背包的容量,而每名学生都会希望按照最大可够买的早餐份数进行购买,即可看作学生的四种选择对应的背包容量分别为0,1,2,3,对应的价值即为他们四种选择对应的最短路。由于涉及的点很少,所以最短路用dfs来处理即可。

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e3+100;
int x[N],y[N],xx[N],yy[N];
ll INF = 1e18;
vector<int> st(5,0);
vector<double> dist(5,INF); 
int n,m,k;

double add(int x1,int y1,int x2,int y2)  //计算两点间的距离
{
    double ans=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
    return ans;
}

void dfs(int a,int b,int c,double cnt) //计算最短路
{
    if(c>3) return; //超过最大食堂数后退出
    dist[c]=min(dist[c],cnt+add(a,b,x[4],y[4])); //最后结果还需加上前往办公室的距离
    for(int i=1;i<=3;i++) //直接枚举3个食堂
    {
        if(!st[i]) 
        {
            st[i]=1;
            dfs(x[i],y[i],c+1,cnt+add(a,b,x[i],y[i]));
            st[i]=0;
        }
    }
}

signed main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);

    vector<vector<double>> dp(N,vector<double>(N,INF));

    cin>>n>>m>>k;
    int b,e; cin>>b>>e;
    for(int i=1;i<=4;i++) cin>>x[i]>>y[i];
    for(int i=1;i<=k;i++) cin>>xx[i]>>yy[i]; 
    b=(n+b-1)/b; e=(m+e-1)/e;

    dp[0][0]=0;
    for(int i=1;i<=k;i++) //枚举每名学生
    { 
        for(int j=0;j<5;j++) st[j]=0,dist[j]=INF; //初始化
        dfs(xx[i],yy[i],0,0); //求四种情况的最短路
        for(int j=0;j<=max(b,e);j++)
        {
            dp[i][j]=min(dp[i-1][j],dp[i-1][j-1]+dist[1]); // 不去或只去一个食堂
            if(j>1) dp[i][j]=min(dp[i][j],dp[i-1][j-2]+dist[2]); //去两个食堂,注意边界问题
            if(j>2) dp[i][j]=min(dp[i][j],dp[i-1][j-3]+dist[3]); //去三个食堂
        }  
    }

    printf("%.10lf\n",dp[k][max(b,e)]);
    return 0; 
}
 

G.Bracelet

题目大意:有00,10(或01),11三种珠子,给你一个字符串,给给定了3种珠子的数量,要求用这些珠子可以拼凑出的字符串中连续子串的最大长度。

思路:由于是手链,头和尾是相连的,所以需要将字符串扩大一倍。每种珠子是两个数字所表示的,所以需要分为奇数偶数两种情况来对字符串进行双指针。

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int N = 1e6+100;
ll INF = 1e18;
int a=0,b=0,c=0; //分别记录每种珠子的使用个数
string s;

void add(int l,int k)
{
    string t; t+=s[l]; t+=s[l+1];
    if(t=="00") a+=k;
    if(t=="01"||t=="10") b+=k;
    if(t=="11") c+=k;
}

signed main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);

    int n,m,k; cin>>n>>m>>k>>s; 
    int t=s.size(); s=s+s;
    int ans=0;

    a=0,b=0,c=0;
    for(int l=0,r=0;r+1<s.size();r+=2)
    {
        add(r,1); //变量右边界
        while(a>n||b>m||c>k||(r-l+2)>t) add(l,-1),l+=2; //当不满足条件时移动左边界
        ans=max(ans,r-l+2);
    }
    
    a=0,b=0,c=0;
    for(int l=1,r=1;r+1<s.size();r+=2)
    {
        add(r,1);
        while(a>n||b>m||c>k||(r-l+2)>t) add(l,-1),l+=2;
        ans=max(ans,r-l+2);
    }
    
    if(t%2) t--; //最大长度不会超出手串本身的长度
    cout<<min(ans,t)<<endl;
    return 0; 
}
 

I.Subnt

题目大意:根据原字符串的反斜杠后的数字,判断给定字符串的前给定数字的二进制串是否相同即可

#include<bits/stdc++.h>
using namespace std;
string f(int x){
	string s;
	while(x){
		s=s+char(x%2+'0');
		x/=2;
	}
	string t;
	int cnt=8-s.size();
	for(int i=1;i<=cnt;i++){
		t+='0';
	}
	s=s+t;
	reverse(s.begin(),s.end());
	return s;
}

int main(){
	int a,b,c,d,e;
	char x;
	cin>>a>>x>>b>>x>>c>>x>>d>>x>>e;
	string s;
	s=s+f(a)+f(b)+f(c)+f(d);
	int q;cin>>q;
	while(q--){
		int ok=0;
		string t;
		cin>>a>>x>>b>>x>>c>>x>>d;
		t=t+f(a)+f(b)+f(c)+f(d);
		for(int i=0;i<e;i++){
			if(s[i]!=t[i]){
				ok=1;
			}
		}
		if(ok) cout<<"NO"<<endl;
		else cout<<"YES"<<endl;
	}
}

J. Iris’Food

题目大意:给你一个m表示你要构造出的数的长度,然后个你10个数分别表示0~9这些数字的个数,让你用这些数字构造出最小的不包含前导零长度为m的数字,需要对结果取模

思路:先看一眼数据范围m可以到1e9,直接暴力枚举一定会超时,我们不妨从要构造的最小的数上进行观察,由于要最小,越是高位的数一定是要越小越好,题目又要求不含前导零,那一定的最高位是一个除零以外的最小数,后面的数一定是按照由小到大的顺序组合。由此我们就可以分别处理连续的相同的段。

记下来的问题是如何处理这些连续的数,我们便可以利用到倍增的特点,对长度为1,2,4,8.....的连续数字进行处理。

i表示长度为2的i次方的连续的数    d[i]=d[i-1]*10^{2^{i-1}}+d[i-1] 

比如我们想要 11111111 8个连续的1,就可以用之前处理出的1111乘上相应的位数

即为 11110000 + 1111 = 11111111

倍增数组处理出来了,那我们如何得到我们想要的长度呢,我们就可以利用倍增数组来做一个二进制拆分,比如我们想要一个长度为7 的数,7的二进制为 111,即为 4+2+1=7;把倍增数组通过同样的方式进行拼接,在处理过程中不断取模即可。我们只需要处理出连续1的数,其他的只需要乘上相应的倍数就可以了。

#include <bits/stdc++.h>
#define ll long long 
#define int long long 
using namespace std;
ll mod=1e9+7;
int a[100],d[100];

ll pow(int a,int b)
{
    if(!b) return 1;
    if(b%2) return a*pow(a,b-1)%mod;
    ll res=pow(a,b/2)%mod;
    return res*res%mod;
}

ll add(int k)
{
    ll ans=0;
    for(int i=30;i>=0;i--)  //二进制拆分从大到小,拼接想得到的数
    {
        if(k>=(1ll<<i))
        {
            k-=(1ll<<i); 
            ans=(ans*pow(10ll,(1ll<<i))%mod+d[i])%mod;
        }
    } 
    return ans%mod;
}

signed main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);

    d[0]=1;   //预处理出连续个1的倍增数组
    for(int i=1;i<=30;i++)
        d[i]=(d[i-1]*pow(10ll,(1ll<<(i-1)))%mod+d[i-1])%mod;

    int T; cin>>T;
    while(T--)
    {
        int n; cin>>n;   
        for(int i=0;i<10;i++) cin>>a[i];
        ll ans=0;

        if(a[0]&&n==1) //构造长度为1的,有0存在的时候需进行特判
        {
            cout<<0<<endl;
            continue;
        }
    
        for(int i=1;i<10;i++)
        { 
            if(a[0]&&a[i]) //先处理除零以外的最小数
            {
                ans=i; a[i]--; n--;
                ans=ans*pow(10ll,min(a[0],n))%mod; n-=a[0]; a[0]=0; 
                if(n<=0) break; 
                ans=(ans*pow(10ll,min(a[i],n))%mod+add(min(a[i],n))*i%mod)%mod;
                n-=a[i]; 
            }
            else if(a[i]) //只需处理连续相同的数即可
            {
                ans=(ans*pow(10ll,min(a[i],n))%mod+add(min(a[i],n))*i%mod)%mod; 
                n-=a[i];
            }
            if(n<=0) break;
        }
        cout<<ans%mod<<endl;
    }
    return 0; 
}
 

K.Welcome

签到题 输出HBCPC2024即可

  • 14
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值