1. 程式人生 > 其它 >牛客2020跨年場 部分題解

牛客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;
}