牛客小白月赛92 题解

牛客小白月赛92 题解

A. 获得木头 签到

\((x\times4)/2\times 4 = x \times 8\)

#include<bits/stdc++.h>

using namespace std;

#define ff first
#define ss second
#define pb push_back
#define all(u) u.begin(), u.end()
#define endl '\n'
#define debug(x) cout<<#x<<":"<<x<<endl;

typedef pair<int, int> PII;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10, M = 105;
const int mod = 1e9 + 7;
const int cases = 0;

void Showball(){
  int x;
  cin>>x;
  cout<<x*8<<endl;    
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T=1;
    if(cases) cin>>T;
    while(T--)
    Showball();
    return 0;
}

B.采矿时间到!签到

优先拿能够消耗1体力的矿石,接着拿与之前拿过的矿石相连的矿石,这样也消耗1体力,最后再拿剩下的矿石,消耗2体力。

void Showball(){
   int n,h;
   cin>>n>>h;
   vector<string> s(5);
   for(int i=0;i<5;i++) cin>>s[i];
   int cnt=0,cnt2=0;
   for(int i=0;i<n;i++){
      if(s[0][i]=='*'){
        if(s[1][i]=='*') cnt+=2;
        else cnt2++;
      }else if(s[1][i]=='*') cnt++;

      if(s[4][i]=='*'){
        if(s[3][i]=='*') cnt+=2;
        else cnt2++;
      }else if(s[3][i]=='*') cnt++;
   }
   int ans=min(h,cnt)+min(max(h-cnt,0)/2,cnt2);
   cout<<ans<<endl;
}

C.耕种时间到!模拟

\(pair\) 保存等级和数量,直接枚举,更新答案即可。

void Showball(){
  int n;
  cin>>n;
  vector<PII> a(n);   
  for(int i=0;i<n;i++){
    int x;
    cin>>x;
    a[i]={x,1};
  }
  int x;
  cin>>x;
  LL ans=0;
  LL maxn=(*max_element(all(a))).ff;
  while(maxn>=x){
    LL tmp=0;
    for(int i=0;i<n;i++){
       if(a[i].ff==x) tmp+=a[i].ss; 
    }
    for(int i=0;i<n;i++) a[i]={(a[i].ff+2)/3,a[i].ss*2};
    maxn=(maxn+2)/3;
    ans=max(ans,tmp);
  }
    cout<<ans<<endl;
}

D. 探索的时光 思维

题意:给你一个数组,请你找一个 \(x (1\le x \le n)\) 满足 \(\sum_{i=1}^n (x-i)^2*a_i\) 的值最小。

思路:展开式子

原式\(=\sum_{i=1}^n (x^2-2*x*i+i^2)*a_i\)

\(=\sum_{i=1}^n (x^2*a_i-2*x*i*a_i+i^2*a_i)\)

因为 \(x\)\(i\) 无关,所以可以拿出来。

原式 \(=x^2*\sum_{i=1}^na_i-2*x*\sum_{i=1}^n i*a_i+\sum_{i=1}^ni^2*a_i\)

那么我们发现除了 \(x\) , 其他部分都是一个常数,那么原式就是一个关于 \(x\) 的二次函数。

可以通过对称轴求解,当然数据范围很小,也可以直接枚举求解即可。

void Showball(){
   int n;
   cin>>n;
   LL a1=0,a2=0,a3=0;
   for(int i=1;i<=n;i++){
     LL x;
     cin>>x;
     a1+=x;
     a2+=i*x;
     a3+=1LL*i*i*x;
   }      
   LL ans=1e18;
   for(int i=1;i<=n;i++){
     ans=min(ans,1LL*i*i*a1-2LL*i*a2+a3);
   }
   cout<<ans<<endl;
}

E. 来硬的 DP

题意:给你 \(n\) 中煤炭,第 \(i\) 块煤炭燃烧 \(y_i\) 秒能够融化至多 \(x_i\) 单位的铁矿石。你有一次使用魔法的机会,可以选择一块煤炭使其燃烧 \(y_i/2\) 秒能够融化至多 \(2x_i\) 单位的铁矿石。 求出烧炼 \(m\) 单位铁矿石至少需要多少时间。

其中 \(1 \le n*m \le 1e6\)

思路:不妨定义\(f_{i,j,0/1}\) 表示前 \(i\) 块煤炭烧炼 \(j\) 单位铁矿石 不使用魔法/使用魔法 的最短时间。

考虑这个状态从哪个状态过来不容易,不妨考虑这个状态可以转移到哪个状态。

那么如果不使用魔法,显然 \(i,j\) 这个状态可以转移到 \(i+1,j+x_{i+1}\)这个状态。并且会花费 \(y_{i+1}\) 的时间

使用了魔法,显然 \(i,j\) 这个状态可以转移到 \(i+1,j+2*x_{i+1}\)这个状态。并且会花费 \(y_{i+1}/2\) 的时间

我们只需要考虑烧炼 \(m\) 单位即可,因此需要和 \(m\) 取个min。具体转移参考代码。

void Showball(){
   LL n,m;
   cin>>n>>m;
   vector<LL> x(n+1),y(n+1);
   for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
   vector<vector<array<LL,2>>> f(n+1,vector<array<LL,2>>(m+1,{inf,inf}));  
   //f[i][j][0/1]表示考虑前i块煤炭烧炼j单位铁矿石 不使用/使用 魔法的 最短时间
   f[0][0][0]=0;
   for(int i=1;i<=n;i++){
    for(int j=0;j<=m;j++){
        f[i][j][0]=min(f[i][j][0],f[i-1][j][0]);
        f[i][j][1]=min(f[i][j][1],f[i-1][j][1]);
        f[i][min(m,j+x[i])][0]=min(f[i][min(m,j+x[i])][0],f[i-1][j][0]+y[i]);
        f[i][min(m,j+x[i])][1]=min(f[i][min(m,j+x[i])][1],f[i-1][j][1]+y[i]);
        f[i][min(m,j+2LL*x[i])][1]=min(f[i][min(m,j+2LL*x[i])][1],f[i-1][j][0]+y[i]/2);
    }
   }
   LL ans=inf;
   for(int i=1;i<=n;i++){
     ans=min(ans,min(f[i][m][0],f[i][m][1]));
   }   
   cout<<ans<<endl;
}

F. 快快乐乐剪羊毛 思维 差分

题意:给你 \(n\) 块草皮,每块草皮的宽度为 \(w_i\),依次连接在一起。每块草皮有一个营养价值 \(v_i\)。现在有 \(m\) 只绵羊,他们的坐标分别为 \(x_i\)。绵羊在对应的草皮上就可以获得对应草皮的营养价值。现在你可以水平向左或者向右平移所有的草皮,求出营养价值之和有多少种不同的取值。

其中 \(1\le n*m \le 1e5\)

思路:直接计算不容易,我们考虑去算每只羊对于草皮的贡献,我们不妨设草皮偏移量为 \(d\),为了方便我们求出每段草皮的覆盖区间,我们可以对 \(w\) 数组前缀和处理即可。这样第 \(j\) 块草皮的覆盖区间就是 \([w_{j-1}+1,w_j]\)

那么显然如果第 \(i\) 只绵羊的位置 \(x_i\) 满足 \(w_{j-1}+1 \le x_i+d \le w_j\) 时,就会产生 \(v_j\) 的贡献。

化简一下式子:\(w_{j-1}+1 -x_i \le d \le w_j -x_i\)

也就是说,只要偏移量符合这个区间,那么第 \(i\) 只绵羊就会产生 \(v_j\) 的贡献。

也就是对这个区间整体加上 \(v_j\)。这样我们就可以用差分进行维护。

考虑使用 \(map\)嵌套 \(vector\) 来存储。枚举所有的情况后,求前缀和然后放进 \(set\) 求种类即可。

代码非常好写,很好的思维题。

void Showball(){
  int n,m;
  cin>>n>>m;
  vector<LL> w(n+1),v(n+1),x(m+1);
  for(int i=1;i<=n;i++){
    cin>>w[i];
    w[i]+=w[i-1];
  }
  for(int i=1;i<=n;i++) cin>>v[i];
  for(int i=1;i<=m;i++) cin>>x[i];
  map<LL,vector<LL>> mp;
  for(int i=1;i<=m;i++){
    for(int j=1;j<=n;j++){
        mp[w[j-1]+1-x[i]].pb(v[j]);
        mp[w[j]-x[i]+1].pb(-v[j]);
    }
  }

  LL cur=0;
  set<LL> st;
  for(auto [k,v]: mp){
    for(auto it:v) cur+=it;
    st.insert(cur);
  }
  cout<<st.size()<<endl;
}
posted @ 2024-05-04 23:35  Showball  阅读(36)  评论(0编辑  收藏  举报