牛客小白月赛27题解
B、 乐***对
题意:给出n个人,将这些人分为若干个组,每个人对应的一个值a[i],表示这个人所在队伍的人数只要有a[i]个人才可以,问最多可以组成多少个队伍?若不能组成队伍,则输出-1.
思路:我们考虑贪心的算法,我们令dp[i]表示当前到i的时候我们可以组成的最多的队伍数量。那么,我们可以直到,当我们到达i的时候,我们要考虑将i分到哪个组里面,我们可以将它们与i-1组成一组,如果i>=a[i]的话,我们也可以让[i-a[i]+1,i]这些组成一组,所以我们可以得到状态转移方程:
所以我们直接进行一个dp即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int a[100010],dp[100010];
int ans=0;
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+1+n);
if(n<a[n]){
cout<<"-1"<<endl;
return 0;
}
for(int i=1;i<=n;i++){
if(i>=a[i]) ans=dp[i-a[i]]+1;
dp[i]=max(ans,dp[i-1]);
}
cout<<ans<<endl;
return 0;
}D、 巅峰对决
题意:给出一个序列,保证序列中每个数字都是不一样的,要求对序列进行单点修改和区间查询,查询区间里面的序列知否可以重新组成一个连续的序列。
思路:一个线段树板子题,可惜比赛时没注意看清题目,我们看到对于每次查询,我们确保我们序列里面的数字都是不同的,那么我们只要查询当前访问序列的最大和最小值,,然后与区间长度进行比较即可判断出来,这样,问题就转化为单点修改+区间查询最大和最小值了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int tree1[N<<2],tree2[N<<2];
int a[N],n,q;
void build(int node,int l,int r){
if(l==r){
tree1[node]=tree2[node]=a[l];
return;
}
int mid=(l+r)>>1,left_node=2*node,right_node=2*node+1;
build(left_node,l,mid);
build(right_node,mid+1,r);
tree1[node]=min(tree1[left_node],tree1[right_node]);
tree2[node]=max(tree2[left_node],tree2[right_node]);
}
void update(int node,int l,int r,int x,int val){
if(l==r){
a[x]=val;
tree1[node]=tree2[node]=val;
return;
}
int mid=(l+r)>>1,left_node=2*node,right_node=2*node+1;
if(x<=mid&&x>=l) update(left_node,l,mid,x,val);
else update(right_node,mid+1,r,x,val);
tree1[node]=min(tree1[left_node],tree1[right_node]);
tree2[node]=max(tree2[left_node],tree2[right_node]);
}
int query_mi(int node,int l,int r,int x,int y){
if(y>=r&&x<=l) return tree1[node];
if(l==r) return tree1[node];
int mid=(l+r)>>1,left_node=node*2,right_node=node*2+1;
int ans=1e9+7;
if(x<=mid) ans=min(ans,query_mi(left_node,l,mid,x,y));
if(y>mid) ans=min(ans,query_mi(right_node,mid+1,r,x,y));
return ans;
}
int query_mx(int node,int l,int r,int x,int y){
if(y>=r&&x<=l) return tree2[node];
if(l==r) return tree2[node];
int mid=(l+r)>>1,left_node=node*2,right_node=node*2+1;
int ans=-1;
if(x<=mid) ans=max(ans,query_mx(left_node,l,mid,x,y));
if(y>mid) ans=max(ans,query_mx(right_node,mid+1,r,x,y));
return ans;
}
int main(){
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
while(q--){
int op,x,y;
cin>>op>>x>>y;
if(op==1){
update(1,1,n,x,y);
}else{
int ans=query_mx(1,1,n,x,y)-query_mi(1,1,n,x,y)+1;
if(y-x+1==ans) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
return 0;
}E、使徒袭来
题意:给三个数的乘积,求出这三个数的和最小。
思路:数学问题,考察基本不等式。
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
double n;
scanf("%lf",&n);
double tmp=pow(n,1.0/3.0);
double ans=3*tmp;
printf("%.3lf\n",ans);
return 0;
}F、核弹剑仙
题意:给出若干个比较,保证在这些比较合法的前提下,输出比每个武器更优的数量。
思路:我们先根据比较来进行建图,如果a优于b的话,我们可以建一条从从b到a的边。然后对于每个点从这该点开始进行一个dfs,即可求出我们的答案
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m,tot,sum;
int head[N],ver[N],nxt[N];
bool vis[N];
void add(int x,int y){
ver[++tot]=y;
nxt[tot]=head[x],head[x]=tot;
}
void dfs(int x){
sum++,vis[x]=true;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(vis[y]) continue;
dfs(y);
}
}
int main(){
cin>>n>>m;
while(m--){
int a,b;
cin>>a>>b;
add(b,a);
}
for(int i=1;i<=n;i++){
sum=-1;
memset(vis,false,sizeof(vis));
dfs(i);
cout<<sum<<endl;
}
cout<<endl;
return 0;
}G、虚空之力
题意:给出一个字符串,对于k,i,n,g可以对答案贡献1,对于k,i,n,g,i,n,g可以为答案贡献2.问答案最大为多少?
思路:一个很显然的贪心,我们肯定要有限满足kinging的情况,只需记录下这些字符的个数然后进行一个合理的分配即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
map<int,int> mp;
int n;
cin>>n;
string s;
cin>>s;
for(int i=0;i<n;i++){
mp[s[i]]++;
}
int ans=0;
while(1){
if(mp['k']==0||mp['i']==0||mp['n']==0||mp['g']==0) break;
else{
if(mp['i']>1&&mp['n']>1&&mp['g']>1){
ans+=2;
mp['i']-=2,mp['n']-=2,mp['g']-=2;
mp['k']--;
}
else{
ans++;
mp['k']--;
mp['i']--,mp['n']--,mp['g']--;
}
}
}
cout<<ans<<endl;
return 0;
}H、社团游戏
题意:给出一个矩阵,找出满足矩阵里面每种字符不超过k个的最大矩阵,输出它的边长。
思路:哈希+二分。我们用num[i][j][k]表示i这个字符在矩阵行1-j,列为1-k的矩阵里面的个数,然后我们在二分答案的时候我们只需要比较是否超过k个即可,这里你也要对二维的哈希有个熟悉的了解。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int num[30][520][520];
char a[520][520];
bool judge(int x,int y,int mid){
for(int i=1;i<=26;i++){
int tx=x+mid-1,ty=y+mid-1;
int sum=num[i][tx][ty]-num[i][x-1][ty]-num[i][tx][y-1]+num[i][x-1][y-1];
if(sum>k) return false;
}
return true;
}
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) cin>>a[i][j];
}
for(int k=1;k<=26;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
num[k][i][j]=num[k][i-1][j]+num[k][i][j-1]+(a[i][j]-'a'+1==k)-num[k][i-1][j-1];
}
}
}
int l,r,mid;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
l=0,r=min(n-i+1,m-j+1);
int ans=0;
while(l<=r){
mid=(l+r)>>1;
// cout<<"mid:"<<mid<<endl;
if(judge(i,j,mid)){
l=mid+1;
ans=mid;
}
else r=mid-1;
}
cout<<ans;
if(j<m) cout<<" ";
}
cout<<endl;
}
return 0;
}I、名作之壁
题意:给你n个数字,第i个数字a[i]表示名作之壁第i天的销量。若某段区间[l,r]中最大值和最小值之差大于k,则称该区间为畅销区间。请问一共有多少个区间为畅销区间?
思路:既然是要对区间内的最大和最小值进行操作的话,我们可以用一个单调队列来对序列进行一个最大和最小值的维护,我们可以枚举每个区间的右端点,如果此时的r是一个畅销区间的话,那么直接对答案的贡献就是n-i+1,因为我们区间的右端点进行一个移动的话,当左端点不动的时候,我们可以知道这个区间里面的最大值肯定是大于等于[l,r]之间的最大值得,然后最小值同理一定是小于等于[l,r]之间的最小值的,那么区间里面的最大与最小值的差一定是大于等于k的,故得证!
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9;
typedef long long ll;
int n,k,b,c;
int a[10000010],q1[10000010],q2[10000010];
int f1=0,f2=0,r1=0,r2=0;
//f1,r1用来维护区间最小值,f2,r2用来维护区间最大值
int main(){
cin>>n>>k;
cin>>a[0]>>b>>c;
ll ans=0;
for(int l=1,r=1;r<=n;r++){
a[r]=((ll)a[r-1]*b+c)%mod;
while(r1>f1&&a[q1[r1-1]]>=a[r]) r1--; //维护最小值
q1[r1++]=r;
while(r2>f2&&a[q2[r2-1]]<=a[r]) r2--; //维护最大值
q2[r2++]=r;
while(a[q2[f2]]-a[q1[f1]]>k){
ans+=n-r+1,l++;
if(q1[f1]<l) f1++; //维护区间长度为正
if(q2[f2]<l) f2++;
}
}
cout<<ans<<endl;
return 0;
}J、逃跑路线
题意:求出一个数组里面的所有数字&&
&
...
$最后所得的值。
思路:这个如果对二进制比较熟悉的话应该很好理解,我们知道上面公式后面&下来之后就是1,所以我们只要对数组里面的最低的和判断一下奇偶性就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
int ans=0;
while(n--){
string a;
cin>>a;
int len=a.size();
ans+=(a[len-1]-'0');
}
if(ans&1) cout<<"1"<<endl;
else cout<<"0"<<endl;
return 0;
}
小天才公司福利 1222人发布