牛客2020跨年场 部分题解

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

2020已经过去了,希望他能够带走我的拖延症和健忘症呀!嘿嘿,今天是2021新年的第一天,努力,奋斗!

A:关于元旦的冷/热知识

题目大意

给出了15个多选题,每行输出答案即可

思路

错了好几次直接劝退了,呜呜呜,事实证明,度娘不靠谱了, 代码是赛后看的,也算是补充了点文化常识吧

AC代码

#include
using namespace std;
#define ll long long
int main()
{
    cout<<"D"<

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
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
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 mp;
	scanf("%d",&t);
	while(t --){
		scanf("%d",&x);
		mp[x] ++;
	}
	if(mp.begin()->first != 1) {
		puts("1");
		return 0;
	}
	vis[1] = 1;
	set 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
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
using namespace std;
#define ll long long
set 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

牛客2020跨年场 部分题解_第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
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;
}

 

你可能感兴趣的:(牛客2020跨年场 部分题解)