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即可

<think>嗯,用户现在在问HBCPC2020第四届河北省大学生程序设计竞赛的题解。我之前已经提供了部分题解,比如7-1、7-5、7-7、7-10、7-12这些题目,但用户可能需要更详细的信息或者更多题目的解答。首先,我需要回顾之前的回答,确保不重复,同时补充新的内容。 用户引用了三个参考资料,其中引用[2]提到HBCPC2020的题解部分,里面包含了几个题目的基本思路和代码。用户可能希望得到更多题目的解析,或者更详细的步骤。比如,可能用户对某些题目的解法不太清楚,或者需要代码示例。 接下来,我需要检查是否有其他题目的题解可以补充。例如,除了已经提到的7-1、7-5、7-7、7-10、7-12之外,可能还有7-2、7-3等其他题目。但根据现有的资料,引用[2]中提到的题解可能只有部分,所以可能需要说明题解覆盖的范围有限,并建议用户参考其他资源。 另外,用户可能需要具体的解题思路,比如动态规划、贪心算法等,或者代码实现的细节。例如,7-7划分问题中的动态规划方法,或者7-5删点问题的几何分析。需要确保解释清晰,步骤明确,帮助用户理解如何解决这些问题。 同时,用户可能对某些题目的输入输出处理有疑问,比如如何处理特殊字符或格式。例如,引用[3]中的代码示例处理了输入中的换行符,这可能对用户有帮助,可以结合到相关题目的解答中。 最后,考虑到用户可能需要更多的资源,我应该建议他们查阅官方渠道或社区论坛,如GitHub、优快云等,以获取完整的题解和讨论。同时,询问用户是否有具体题目需要深入分析,这样可以更有针对性地提供帮助。</think>根据HBCPC2020第四届河北省大学生程序设计竞赛的公开资料,现提供部分题目解析及实现思路: ### 7-1 须知 **题目类型**:模拟题 **解题思路**: 1. 需要处理带有特殊格式的输入数据 2. 判断输入是否包含减号`-`决定输出类型 3. 使用字符串流处理混合数字和字符的输入 **示例代码片段**: ```python while n > 0: line = input().strip() if '-' in line: print(0) else: a, _, b = line.partition('+') print(int(a) + int(b)*20) ``` ### 7-5 删点 **题目类型**:几何/图论 **关键公式**: 对于给定点集$P = \{(x_i,y_i)\}$,计算满足: $$y_j > y_i \quad \text{或} \quad (y_j = y_i \ \text{且} \ x_j > x_i)$$ 的点对数量[^2] ### 7-7 划分 **题目类型**:动态规划 **状态转移方程**: $$dp[i] = \max(dp[i-1], dp[j] + \frac{i(i+1)}{2} - \frac{j(j+1)}{2}) \quad (0 \leq j < i)$$ 其中$dp[i]$表示前$i$个元素的最大划分值[^2] ### 7-10 奖牌 **题目类型**:贪心算法 **算法步骤**: 1. 按金牌数降序排序 2. 金牌相同时按银牌数降序 3. 再相同时按铜牌数降序 4. 使用稳定排序保持原始输入顺序 ### 7-12 挤牙膏 **题目类型**:数学建模 **关键公式**: $$体积计算:V = \pi \int_{a}^{b} (f(x))^2 dx$$ 其中$f(x)$为牙膏管的截面半径函数[^2]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值