牛客小白月赛99
A 材料打印
题目大意
打印的材料有a页可以黑白,也可以彩印,而b页只能菜印。黑白x元,彩印y元。计算需要的最少钱
解题思维
当x>=y时(即黑白>=彩印) 显然答案为(a+b)*y
当x<y时(即黑白<彩印),答案为a*x+b*y
解题代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
void solve()
{
int a,b,x,y;
cin>>a>>b>>x>>y;
if(y<=x)
cout<<(a+b)*y<<"\n";
else
{
cout<<a*x+b*y<<"\n" ;
}
}
signed main()
{
int t;
cin>>t;
while(t--)
solve();
}
B %%%
题目大意
求出使得操作n=n%mod(1<=n<=mod)时,重复操作多次,使得n变为0时,所需要的最多操作次数
从1到15有。
解题思维
1:1次
2:1次
3:%2=1 2次
4:%3 =1 2次
5:%3=2 2次
6:%4=2 2次
7:%4=3 3次
8:%5=3 3次
9:%5=4 3次
10:%6=4 3次
11: %6=5 3次
12:%7=5 3次
13:%7=6 3次
14:%8=6 4次
15:%8=7 5次
发现规律 想让一个数的操作次数最大,每次更新它为它取(它一半+1)的模即可,
解题代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
ll n;
cin>>n;
ll ans=1;
if(n==0)
{
cout<<0<<"\n";
return;
}
while(n!=1&&n!=2)
{
n%=((n>>1)+1);
ans++;
}
cout<<ans<<"\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int tt;
std::cin >> tt;
while (tt--) {
solve();
}
return 0;
}
C 迷宫
题目大意
选择性开拓一条横向或纵向的路,看是否能使得入口能到出口。
解题思维
可以先bfs(广度优先搜索)将出口能到达的点,并标记可以联通的开拓路尝试开拓路,全部标记上,如果发现入口被标记了,则表示YES
如果没表示YES
则继续bfs入口,看能否联通出口,如果能联通则表示找到了
接替代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 1010
//记忆探索过的x.y,以及标记过的点
int stx[N],sty[N],n,m,vis[N][N],f1,f2,e1,e2;
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
char a[N][N];
void cb(){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
//记录起点
if(a[i][j]=='S')f1=i,f2=j;
//记录出口
else if(a[i][j]=='E')e1=i,e2=j;
}
}
//对出口进行bfs
queue<pair<int,int>>q;
q.push({e1,e2});
while(!q.empty()){
int x=q.front().first,y=q.front().second;
q.pop();
if(x<1||x>n||y<1||y>m||vis[x][y])continue;
vis[x][y]=2;
//记录可以被联通的开拓路
stx[x]=stx[x+1]=stx[x-1]=1,sty[y]=sty[y+1]=sty[y-1]=1;
for(int i=0;i<4;i++){
int nx=x+dx[i],ny=y+dy[i];
if(nx<1||nx>n||ny<1||ny>m)continue;
if(a[nx][ny]!='#'&&!vis[nx][ny])q.push({nx,ny});
}
}
bool ok=(vis[f1][f2]==2);
if(ok)
{
cout<<"YES";
return 0;
}
q.push({f1,f2});
//对入口进行bfs,如果找到可以开拓的路,表明,找到答案
while(!q.empty()){
int x=q.front().first,y=q.front().second;
q.pop();
if(vis[x][y]==1)continue;
vis[x][y]=1;
if(sty[y])ok=true;
if(stx[x])ok=true;
if(ok)
break;
for(int i=0;i<4;i++){
int nx=x+dx[i],ny=y+dy[i];
if(nx<1||nx>n||ny<1||ny>m)continue;
if(vis[nx][ny]==2)ok=true;
if(a[nx][ny]!='#'&&vis[nx][ny]!=1)q.push({nx,ny});
}
}
if(ok)cout<<"YES";
else cout<<"NO";
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
int TT;TT=1;
while(TT--)cb();
return 0;
}
D 又是一年毕业季
题目大意
不同学生可能会隔不同的时间眨眼,默认0秒都眨眼了,求出保证所有学生不眨眼的最小时间ans,ans需要大于>=2
解题思维
由于答案肯定出在质数上面,所以首先,将从2到n的所有质数进行求出,并将不是质数的数进行标记,将质数进行存储在a。
随后在输入的数据中,将是质数的数进行存储在b。
同开头遍历a和b,当值不一样时找到答案
解题代码
//Mercy
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 7, INF = 0x3f3f3f3f;
vector<int> prime;
bool a[INF];
void init()
{
//将质数进行存储,非质数进行标记
for(int i=2;i<N;i++)
{
if(!a[i])
prime.push_back(i);
for(int j=i*2;j<N;j+=i)
a[j] = true;
}
}
void solve()
{
set<int>s;
ll n;
cin>>n;
while(n--)
{
int x;
cin>>x;
//将质数进行存储
if(x<=INF&&!a[x]) s.insert(x);
}
int t = 0;
//找到第一个不一样的质数
for(auto w : s)
{
if(w!=prime[t])
{
cout<<prime[t]<<endl;
return;
}
t++;
}
//如果都不符合,显然在它的后一位质数
cout<<prime[t]<<endl;
return ;
}
int main() {
init();
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll t;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
E 多米诺骨牌
题目大意
推到m个骨牌,使得能推倒 最多的骨牌
解题思维
首先将各个牌按照位置进行排序。然后依次从头尝试推倒牌,如果有牌被推倒,那么它就不会在被遍历了,当没有可以被推倒的牌时,记录当前推倒牌的数量,然后继续遍历下一个牌。最后对存储的树进行降序排序,取前m个,即为答案。
解题代码
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
using pil = pair<int ,int >;
void solve()
{
int n,m;
cin>>n>>m;
pil v[n+2];
vector<int> num_;
for(int i=1;i<=n;i++)
cin>>v[i].se;
for(int i=1;i<=n;i++)
cin>>v[i].fi;
sort(v+1,v+n+1);
for(int i=1;i<=n;i++)
{
int j=i;
int vs = v[i].fi+v[i].se;
while(vs>=v[i].fi)
{
// cout<<j<<"推到"<<i<<"\n";
//更新当前所能推倒的最大位置
vs = max(v[i].fi+v[i].se,vs);
i++;
if(i==n+1)
break;
}
//记录推倒的牌数
num_.push_back(i-j);
if(i==n+1)
break;
i--;
}
sort(num_.begin(),num_.end(),greater<int>());
int h = num_.size();
int ns = min(h,m);
int ans=0;
for(int i=0;i<ns;i++)
{
ans+=num_[i];
}
cout<<ans<<"\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
while(T--) solve();
}
F 自爆机器人
题目大意
使用在m个可以重复建筑或销毁的建筑,使得炸弹威力最大。
解题思维
为了爆炸,显然要至少要满足n
那么d = t-n
希望求解的就是在d的范围内,所能来回的最大值。也可以理解为一个长度内,用多个线段,尽量覆盖最大长度内范围的问题。
由此可以将,每个建筑物位置进行排序,n>=i>=2,求出 (ai-ai-1)*2的长度。尝试使得在d范围内,所能覆盖的最大范围,使用dp(动态规划)的思想进行求解
解题代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, m, t;
int x[N];
int ans;
int dp[N * 2], cnt;
set<int> st;
signed main()
{
int T;
cin>>T;
while(T -- )
{
ans = 0;
//用于记录可以使用的线段
st.clear();
cin>>n>>m>>t;
for(int i = 1; i <= m; i ++ ) cin>>x[i];
sort(x + 1, x + 1 + m);
if(t < n)
{
cout<<0<<endl;
continue;
}
int d = t - n;
ans = n;
for(int i = 1; i <= d; i ++ ) dp[i] = 0;
//找到所以可以使用的线段
for(int i = 2; i <= m; i ++ )
{
int len;
len = x[i] - x[i - 1];
len *= 2;
st.insert(len);
}
//进行dp
for(auto x : st)
{
//使用x尝试求出到达某个长度时,所能使用的所有线段的最大长度
for(int j = x; j <= d; j ++ )
{
dp[j] = max(dp[j], dp[j - x] + x);
}
}
ans += dp[d];
cout<<ans<<endl;
}
return 0;
}
G 大鱼吃小鱼
题目大意
求出在某一时刻,使得鱼重量最大
解题思维
使用扫描线算法找到在某一时刻的所有鱼的信息。
通过树状数组,对满足条件的信息,进行答案的更新
解题代码
#include<bits/stdc++.h>
#define lowbit(x) x&-x
using namespace std;
const int maxn=2e5+5;
long long tree[maxn];
void add(int id,int num)
{
while(id<=200000)
{
tree[id]+=num;
id+=lowbit(id);
}
}
long long sum(int id)
{
long long num=0;
while(id)
{
num+=tree[id];
id-=lowbit(id);
}
return num;
}
struct fish
{
int l,r,y;
}fishs[maxn];
struct opt
{
int yid,val,time;
bool operator<(opt a)
{
return time<a.time;
}
}opts[maxn];
void solve()
{
vector<int>w;
int n,x;
cin>>n>>x;
//首先存储所有鱼的信息
//并记录好鱼的重量信息
for(int i=1;i<=n;i++)
{
cin>>fishs[i].l>>fishs[i].r>>fishs[i].y;
w.push_back(fishs[i].y);
}
//升序排序并进行去重
sort(w.begin(),w.end());
int ed=unique(w.begin(),w.end())-w.begin();
//扫描线
for(int i=1;i<=n;i++)
{
//记录第一个大于该鱼重量的重量索引
int yid = lower_bound(w.begin(),w.begin()+ed,fishs[i].y)-w.begin()+1;
opts[2*i-1]=opt({yid,fishs[i].y,fishs[i].l});
opts[2*i]=opt({yid,-fishs[i].y,fishs[i].r});
}
//根据时间进行升序排序
sort(opts+1,opts+2*n+1);
int tail=1;
long long ans=x;
//进行扫描
while(tail<=2*n)
{
//对相同时刻的鱼,进行处理
do{
//对大于该鱼的第一个索引以及之后的所有鱼加上该鱼的重量
add(opts[tail].yid,opts[tail].val);
tail++;
}while(tail<=2*n&&opts[tail].time==opts[tail-1].time);
long long last=0;
long long nowx=x;
while(1)
{
//找到第一个小于当前鱼重量的鱼的重量索引
int xid = upper_bound(w.begin(),w.begin()+ed,nowx)-w.begin();
//求出在重量为x的基础上,能得到的重量。
long long addval=sum(xid);
//如果继续求重量,发现没有更新
if(addval-last==0)
break;
else
{
//对鱼的当前重量进行更新,并尝试求能否到达新的重量
nowx+=addval-last;
last=addval;
}
}
//对每个时间点,都尝试进行答案的更新。
ans=max(ans,nowx);
}
cout<<ans<<"\n";
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t=1;
cin>>t;
while(t--)
solve();
return 0;
}