牛客2020跨年場 部分題解
題目連結:https://ac.nowcoder.com/acm/contest/9854
2020已經過去了,希望他能夠帶走我的拖延症和健忘症呀!嘿嘿,今天是2021新年的第一天,努力,奮鬥!
A:關於元旦的冷/熱知識
題目大意
給出了15個多選題,每行輸出答案即可
思路
錯了好幾次直接勸退了,嗚嗚嗚,事實證明,度娘不靠譜了, 程式碼是賽後看的,也算是補充了點文化常識吧
AC程式碼
#include<bits/stdc++.h> using namespace std; #define ll long long int main() { cout<<"D"<<endl;//1 cout<<"ABCD"<<endl;//2 cout<<"D"<<endl;//3 cout<<"A"<<endl;//4 cout<<"ABC"<<endl;//5 cout<<"C"<<endl;//6 cout<<"B"<<endl;//7 cout<<"C"<<endl;//8 cout<<"D"<<endl;//9 cout<<"D"<<endl;//10 cout<<"A"<<endl;//11 cout<<"C"<<endl;//12 cout<<"C"<<endl;//13 cout<<"D"<<endl;//14 cout<<"AD"<<endl;//15 return 0; }
B:牛牛想起飛
題目大意
給兩個序列ab,相當於構造一個序列c,其中c[i]要麼是a[i],要麼是a[i]+b[i],要麼是a[i]-b[i],現在要求c的總和對y取模的最大值
思路
算是一個較基礎的dp吧,思想跟01揹包差不多,dp[i][j]做標記用,表示對於c陣列從1到i求和取模存不存在j,最後只需要在dp[n][j]當中找到最大的j使得dp[n][j]=1即可
dp[i - 1][(j - d1 + y) % y]表示如果當前c[i]取a[i],那麼為了得到j,那麼就得看1到i-1求和能不能得到j-d1了,剩下兩個同理。
AC程式碼
#include<bits/stdc++.h> using namespace std; #define ll long long #define lowbit(x) x&(-x) const int N = 1e5 + 5; const int M = 105; int dp[N][M], a[N], b[N]; int main(){ int n, y; scanf("%d%d",&n, &y); for(int i = 1; i <= n; i ++) scanf("%d",&a[i]); for(int i = 1; i <= n; i ++) scanf("%d",&b[i]); for(int i = 1; i <= n; i ++){ int d1, d2, d3; // c陣列的三種情況 d1 = a[i] % y; d2 = (a[i] - b[i] + y) % y; d3 = (a[i] + b[i]) % y; dp[i][d1] = dp[i][d2] = dp[i][d3] = 1; for(int j = 0; j < y; j ++){ if(dp[i - 1][(j - d1 + y) % y] || dp[i - 1][(j - d2 + y) % y] || dp[i - 1][(j - d3 + y) % y]) dp[i][j] = 1; } } int d = y - 1; //取模最大值是y-1 while(!dp[n][d]) d --; printf("%d", d); return 0; }
C:最小互質數
題目大意
給你n個數,求最小的數t,使得t跟這n個數都互質。
思路
首先如果n個數中沒有1那麼1跟他們都互質,所以直接輸出1好了。
我是先篩出所有質因子,然後拿這些質因子標記所有他們的倍數,這些數都不能選,因為都不互質的,然後差不多就是字首和加二分找mex的思想找答案
後來發現是我想太複雜了,可以直接邊篩素數邊找答案的,學到了~
AC程式碼
質因子分解+字首和+二分
#include<bits/stdc++.h> using namespace std; #define ll long long #define lowbit(x) x&(-x) const int maxn = 1e5 + 5; int vis[maxn], sum[maxn]; int main(){ int t, x; map<int, int> mp; scanf("%d",&t); while(t --){ scanf("%d",&x); mp[x] ++; } if(mp.begin()->first != 1) { puts("1"); return 0; } vis[1] = 1; set<int> s; //存質因子 for(auto it : mp){ //遍歷mp int d = it.first; if(d == 1) continue; for(int i = 2; i * i <= d; i ++){ if(d % i == 0) { while(d % i == 0) d /= i; s.insert(i); } } if(d > 1) s.insert(d); } for(auto it : s){ int tmp = it; while(tmp < maxn){//標記質因子的倍數 vis[tmp] = 1; tmp += it; } } for(int i = 1; i < maxn; i ++) { sum[i] = sum[i - 1] + vis[i];//求字首和 } int l = 1, r = maxn, ans = -1; while(l <= r){ int mid = l + r >> 1; if(sum[mid] < mid) ans = mid, r = mid - 1; else l = mid + 1; } printf("%d",ans); return 0; }
邊質數篩邊找答案
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5 + 5;
int vis[maxn];
int prime[maxn];
int work(){
if(vis[1] == 0) return 1;
for(int i = 2; i < maxn; i ++){
int flag = 0;
if(prime[i]) continue;
for(int j = 1; j * i < maxn; j ++){
prime[i * j] ++;
if(vis[i * j]) flag = 1;
}
if(!flag) return i;
}
return -1;
}
int main(){
int n; scanf("%d",&n);
for(int i = 1; i <= n; i ++) {
int x; scanf("%d",&x);
vis[x] = 1;
}
printf("%d", work());
return 0;
}
D:銜尾蛇
題目大意
有三種蛇,現在來構造銜尾蛇, 可以選不超過n條蛇,讓他們首尾連線形成環,問可以構造多少種環
思路
蛇的數量不大才12,可直接進位制列舉,三進製表示三個狀態,再判斷是否合法即可
AC程式碼
#include<bits/stdc++.h>
using namespace std;
#define ll long long
set<string> s;
int main(){
int a, b, c, n, sum = 1;
scanf("%d%d%d",&a, &b, &c);
n = a + b + c;
for(int j = 1; j <= n; j ++){ //列舉蛇的數量
sum *= 3;
for(int i = 0; i < sum; i ++){ //狀態列舉
int p = i, ca = 0, cb = 0, cc = 0;
string tmp = "";
for(int k = 0; k < j; k ++){ //該狀態下選的蛇, tmp是構成的環
if(p % 3 == 0) tmp += "a", ca ++;
if(p % 3 == 1) tmp += "b", cb ++;
if(p % 3 == 2) tmp += "c", cc ++;
p /= 3;
}
if(ca > a || cb > b || cc > c) continue; //判斷該狀態選的蛇是否合法
string ans = tmp + tmp; //由於是環形那麼就加一層,然後滑動視窗一樣取長度是j的那麼就是環的所有情況了
int flag = 0;
for(int k = 0; k < j; k ++) {
if(s.count(ans.substr(k, j))){ //判斷之前是否存在過
flag = 1;
break;
}
}
if(!flag) s.insert(tmp); //不存在,那麼記錄答案
}
}
printf("%d\n", s.size());
return 0;
}
E:牛牛的反函式
題目大意
有一個函式F(x)表示x是奇數是,那麼F(x)=F(x+1)+1,否則F(x)=F(x/2)+1
現在給你一個F(x)的值,讓你輸出任意一個x,如果不存在則輸出-1
思路
暴力跑狀態,發現函式值最大也就120。
a[i]表示F(x)=i的那個x值,也就是說F(a[i]) = i。
我是這麼構造的,其實就是反函式的思想,函式的逆過程:
前幾個可以初始化a[1]=1,a[2]=2,a[3]=3
如果a[i-1]是奇數時,那麼a[i]應該是a[i-1]乘2了,比如說F(3)是4,3的下一個狀態是F(6)是由3得到的, 那麼F(6) 是5,在這裡反推就是a[4]=3,a[5] = a[4] * 2 = 6
如果a[i-1]是偶數時,那麼a[i]應該是a[i-1]減1了,比如說F(4)是3,那麼下一個狀態F(3)是由4得到的,那麼F(3)是4,在這裡反推就是a[3]=4, a[4] = a[3] - 1 = 3
當a[i]大於1e18的時候可以退出。
AC程式碼
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 200;
ll a[maxn];
int main(){
int t, i; scanf("%d", &t);
a[1] = 1; a[2] = 2; a[3] = 4;
for(i = 4;; i ++){
if(a[i - 1] & 1) a[i] = a[i - 1] * 2;
else a[i] = a[i - 1] - 1;
if(a[i] > 1e18) break;
}
while(t--){
ll n; scanf("%lld",&n);
if(n >= i) puts("-1");
else printf("%lld\n", a[n]);
}
return 0;
}