牛客周赛 Round 82 D/E/F

D-小苯的排列技术

原题链接:https://ac.nowcoder.com/acm/contest/102303/D

思路:

  1. 先判断序列是否有可行方案,即判断序列为非递增序列且最后一位一定为1。
  2. 在数字 \(P\) 在序列中出现的第一个位置即为其应该放置的位置,并且在下一个数字在序列中出现的位置与当前 \(P\) 所在位置间的个数,即为所要填的值要大于 \(P\) 的数字的个数。
    \(cnt\) 为序列前缀中已经出现的数字的个数、\(sum\) 为已经填过的数字的个数、\(x\)\(P\) 出现的次数减一,则此时大于 \(P\) 且可选数字的个数 \(t\)\(n-p-cnt-sum\)
    如此时t的值小于0,那么不合法答案为0,否则答案要乘上 \(C(t,x)*A(x,x)\)

时间复杂度:\(O(n)\)

代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int mod=998244353;

int n;
vector<int> pre;

bool check(){
  int last=pre[0];
  for(int i=1;i<n;i++){
    if(last<pre[i]) return false;
    last=pre[i];
  }
  return last==1;
}

void solve(){
  cin>>n;
  
  pre=vector<int>(n);
  for(int i=0;i<n;i++)cin>>pre[i];
  
  if(!check()){
    cout<<0<<endl;
    return;
  }
  
  ll ans=1;
  int cnt=0,sum=0;
  
  for(int i=0;i<n;i++){
    int p=pre[i],j=i;
    while(j<n&&pre[j+1]==p)j++;
    
    int x=j-i;
    int t=n-p-cnt-sum;
    
    if(t<0){
      ans=0;
      break;
    }
    
    for(int i=t-x+1;i<=t;i++)ans=ans*i%mod;
    
    cnt++,sum+=x;
    i=j;
  }
  
  cout<<ans<<endl;
}

int main(){
  cin.tie(0)->sync_with_stdio(false);
  cout.tie(0);
  
  int t;
  cin>>t;
  
  while(t--)solve();
}

E-和+和

题目链接:https://ac.nowcoder.com/acm/contest/102303/E

思路:

\(a\) 序列的最大索引小于 \(b\) 序列的最小索引,可以固定 \(a\) 序列最大索引的位置,先分别处理 \(a\) 前缀 \(b\) 后缀的的 \(m\) 项最小和,这个操作可以通过维护大根堆来进行计算,然后遍历所有 \(a\) 的最大索引可能的位置找到答案。

时间复杂度:\(O(nlog(m))\)

代码

// 前m小和: 维护前m小的小根堆即可 每次动态对值进行维护

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

int n,m;

int main(){
  cin.tie(0)->sync_with_stdio(false);
  cout.tie(0);
  
  cin>>n>>m;
  
  vector<ll> a(n+1),b(n+1);
  for(int i=1;i<=n;i++)cin>>a[i];
  for(int i=1;i<=n;i++)cin>>b[i];
  
  vector<ll> prea(n+1,0),sufb(n+1,0);
  
  priority_queue<ll,vector<ll>,less<ll>> ha,hb;
  
  ll suma=0,sumb=0;
  
  for(int i=1;i<=n;i++){
    ha.push(a[i]);
    suma+=a[i];
    if(ha.size()>m){
      suma-=ha.top();
      ha.pop();
    }
    prea[i]=suma;
  }
  
  for(int i=n;i;i--){
    hb.push(b[i]);
    sumb+=b[i];
    if(hb.size()>m){
      sumb-=hb.top();
      hb.pop();
    }
    sufb[i]=sumb;
  }
  
  ll ans=1e18;
  for(int i=m;n-i>=m;i++){
    ans=min(ans,prea[i]+sufb[i+1]);
  }
  
  cout<<ans;
}

F-怎么写线性SPJ

原题链接:https://ac.nowcoder.com/acm/contest/102303/F

思路:

思维题,因为整个序列一定有单个元素,不妨为\(1\),那么我们将\(1\)放到构造的序列的中间位置,然后分别考虑被分开的两部分,选取单独一部分也一定有单个元素,并且可相同,因为若同时选取,那么一定会把1包含在内,一定符合题意,那么答案是类似:\(... 4 3 4 2 4 3 4 1 4 3 4 2 4 3 4 ...\) 这样的序列,其中 \(1\) 最多一个,\(2\) 最多两个,\(3\) 最多4个... \(n\) 最多 \(2^(n-1)\) 个。那么最少种类数就可以找到,然后序列可以编写递归程序去构造。

时间复杂度 O(n)

代码:

#include<bits/stdc++.h>
using namespace std;

void dfs(vector<int> &a,int l,int r,int c){
  if(l>r) return;
  int mid=l+r>>1;
  a[mid]=c;
  dfs(a,l,mid-1,c+1),dfs(a,mid+1,r,c+1);
}

int main(){
  cin.tie(0)->sync_with_stdio(false);
  cout.tie(0);
  
  int n;
  cin>>n;
  
  int t=0,p=1,s=0;
  while(s<=n){
    s+=p;
    t++;
    p<<=1;
  }
  
  cout<<t<<endl;
  
  vector<int> ans(n+1,0);
  dfs(ans,1,n,1);
  
  for(int i=1;i<=n;i++)cout<<ans[i]<<' ';
}
posted @ 2025-02-24 19:39  宋佳奇  阅读(17)  评论(0)    收藏  举报