【题解】牛客模考 · 大厂定制出题模拟笔试
A式计数法
难度:1星
知识点:模拟,字符串
题意:对于每个给定数字,使用A式计数法输出。
按照题意模拟即可。注意倒序处理。每隔A个字符输出一个'_'字符即可。
import java.util.*; public class Main{ public static void main(String[] args){ Scanner in = new Scanner(System.in); int t=in.nextInt(); for(int i=0;i<t;i++){ int a=in.nextInt(); String s=in.next(),out=""; int cnt=0; for(int j=s.length()-1;j>=0;j--){ if(cnt>0&&cnt%a==0)out='_'+out; cnt++; out=s.charAt(j)+out; } System.out.println(out); } } }
质数之手
难度2.5星
知识点:数论、枚举
题意:给定n,找到最小一个正整数是n的数位重排、且是素数。若不存在则输出0
思路: 考虑到n不超过1e6,所以只需要把所有素数筛出来,然后枚举即可。
需要考虑以下限制条件:
-
必须是素数(可以用筛法解决,O(1)判断)
- 必须是n的重排(可以利用桶统计每个数的次数,O(10)判断)
import java.util.*; public class Main{ static boolean jud(int a,int b){ //判断是否构成重排 int[] A=new int[10],B=new int[10]; while(a>0){ A[a%10]++; a/=10; } while(b>0){ B[b%10]++; b/=10; } for(int i=0;i<10;i++)if(A[i]!=B[i])return false; return true; } static boolean[] pr; public static void main(String[] args){ Scanner in = new Scanner(System.in); int n=in.nextInt(); pr = new boolean[1000010]; for(int i=1;i<=1e6;i++)pr[i]=true; pr[1]=false; for(int i=2;i<=1e6;i++){ if(pr[i]){ for(int j=i*2;j<=1e6;j+=i){ pr[j]=false; } } } ArrayList<Integer> out = new ArrayList<Integer>(); for(int i=1;i<=1e6;i++){ if(jud(n,i)&&pr[i])out.add(i); } System.out.println(out.size()); if(out.size()>0){ System.out.println(out.get(0)); } } }
合成二叉树
知识点:贪心、二叉树
难度:3.5星。
思路:
使用贪心策略,优先合并两个节点数最小的树一定是最优解。
因此按题意模拟,把所有树放入优先队列,每次优先选择两个节点数最小的树进行合并即可。合并后再加入优先队列。
/** * struct TreeNode { * int val; * struct TreeNode *left; * struct TreeNode *right; * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * }; */ map<TreeNode*,int>siz; map<TreeNode*,int>id; class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param T TreeNode类vector * @return TreeNode类 */ int getSiz(TreeNode *T) { if(T==nullptr) return 0; return getSiz(T->left)+getSiz(T->right)+1; } TreeNode* mergeTree(vector<TreeNode*>& T) { // write code here auto cmp=[](TreeNode*& a, TreeNode*& b) { if(siz[a]==siz[b]) return id[a]>id[b]; else return siz[a]>siz[b]; }; priority_queue<TreeNode*,vector<TreeNode*>,decltype(cmp)>q(cmp); int n=T.size(); for(int i=0;i<n;i++) { siz[T[i]]=getSiz(T[i]); id[T[i]]=i; q.push(T[i]); } while(q.size()!=1) { TreeNode *a=q.top(); q.pop(); TreeNode *b=q.top(); q.pop(); if(siz[a]!=siz[b]) { TreeNode *c=new TreeNode(1); if(siz[a]>siz[b]) swap(a,b); c->left=a; c->right=b; siz[c]=siz[a]+siz[b]+1; id[c]=n++; q.push(c); } else { TreeNode *c=new TreeNode(1); c->left=a; c->right=b; siz[c]=siz[a]+siz[b]+1; id[c]=n++; q.push(c); } } return q.top(); } };
插班生
难度:5星
思路:
对于每个节点,我们可以通过预处理的方式达成 O(1) 的查询。
预处理方式如下:我们先只考虑插班生右方的学生。
如果第一个位置是插班生,那么右边的依次是 -(n-1) -(n-2),...,-2,-1
当插班生右移时,插班生右边的依次是 -(n-2),...,-2,-1
我们可以发现,当一个点会减成负的时,如果再将插班生右移,我们会发现剩下的一定都是负的。
因此可以把每个点的“会减成负的”的区间求出来,利用差分、前缀和达成区间的合并。这样就能批量处理,达成O(1)查询了。
对于插班生左边的学生,处理方式同右边。
#include <iostream> using namespace std; const int maxn = 1e5 + 5; #define ll long long #define int long long ll pre[maxn], suf[maxn], pre2[maxn], suf2[maxn], sum[maxn], a[maxn]; signed main(){ int n; cin >> n; ll ans = 0; for(int i = 1; i <= n; i++){ cin >> a[i]; ans += a[i]; } for(int i = 1; i <= n; i++){ if(a[i] >= n - 1) continue; ll l = i - (n - a[i] - 1), d = 0; if(l < 1){ d = - l + 1; l = 1; } pre[l]++; pre[i]--; pre2[l] += d; pre2[i] -= d + (i - l); } for(int i = n; i >= 1; i--){ if(a[i] >= n - 1) continue; ll r = i + (n - a[i] - 1), d = 0; if(r > n){ d = r - n; r = n; } suf[r]++; suf[i]--; suf2[r] += d; suf2[i] -= d + (r - i); } for(int i = 1; i <= n; i++){ pre[i] = pre[i-1] + pre[i]; pre2[i] += pre[i] + pre2[i-1]; } for(int i = n; i >= 1; i--){ suf[i] = suf[i+1] + suf[i]; suf2[i] += suf[i] + suf2[i+1]; } // for(int i = 1; i <= n; i++){ // cout << suf2[i] << " "; // } for(int i = 1; i <= n; i++){ cout << ans - a[i] - (i-1) * i / 2 - (n-i) * (n-i+1) / 2 + suf2[i] + pre2[i] - 2 * (n - i) * (i - 1) << " "; } }