原题地址 :Dashboard - The 8th Hebei Collegiate Programming Contest - Codeforces
目录
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次方的连续的数
![]()
比如我们想要 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即可