YCU月赛题解

题解只供参考,哪里有问题欢迎提出来

A:朱朱的斐波那契数列

n比较大,卡你时间,采用构造矩阵,再用矩阵快速幂解决,不过这样还是有点问题,因为mod值比较大,还需考虑一个快速乘,这题最难的点在于如何构造矩阵,在此贴上一篇博客
https://blog.csdn.net/Akatsuki__Itachi/article/details/80443939

#include
using namespace std;
typedef long long ll;
typedef struct {
	ll a[4][4];
}matrix;
const ll mod = 1e10+19;
ll add(ll a, ll b, ll c){  //快速乘
	ll r = 0;
	while(b){
		if(b & 1) r = (r+a)%c;
		a = (a*2)%c;
		b /= 2;
	}
	return r;
}
matrix mul(matrix p, matrix q){ //4阶矩阵乘法
	matrix d;
	for(ll i = 0; i < 4; i++){
		for(ll j = 0; j < 4; j++){
			d.a[i][j] = 0;
			for(ll k = 0; k < 4; k++){
				d.a[i][j] += add(p.a[i][k],q.a[k][j],mod); //考虑mod值过大采用快速乘
				d.a[i][j] %= mod;
			}
		}
	}
	return d;
}
matrix kmi(matrix p, ll num){ //矩阵快速幂
	matrix res;
	memset(res.a,0,sizeof(res.a));
	for(int i = 0; i < 4; i++){ //构造单位矩阵
		res.a[i][i] = 1;
	} 
	while(num){
		if(num & 1) res = mul(res,p);
		num /= 2;
		p = mul(p,p);
	}
	return res;
}
int main(){
	ll n;
	cin >>  n;
	matrix b, c;
	memset(b.a,0,sizeof(b.a));
	memset(c.a,0,sizeof(c.a));
	b.a[0][1] = 1;
	b.a[1][0] = 1;
	b.a[1][1] = 1;
	b.a[2][1] = 1;
	b.a[2][2] = 1;
	b.a[3][1] = 1;
	b.a[3][2] = 1;
	b.a[3][3] = 1;
	c.a[0][0] = 1;
	c.a[0][1] = 1;
	c.a[0][2] = 3;
	c.a[0][3] = 1;
	b = kmi(b,n-1);
	c = mul(c,b);
	cout << c.a[0][0] << endl;
	return 0;
}

B:送温暖的简单签到题

这个题目考的数学里面的容斥原理
两个集合的容斥关系公式:A∪B = A+B - A∩B (∩:重合的部分)
三个集合的容斥关系公式:A∪B∪C = A+B+C - A∩B - B∩C - C∩A +A∩B∩C
四个有限集合 :A∪B∪C∪D=A+B+C+D- A∩B - B∩C - C∩A- A∩D - B∩D -C∩D+A∩B∩C+A∩B∩D +A∩C∩D +B∩C∩D -A∩B∩C∩D

#include
using namespace std;
typedef long long ll; 
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	ll n, a, b, c, d, sum;
	cin >> n;
	cin >> a >> b >> c >> d;
	sum = n/a + n/b + n/c + n/d - n/a/b - n/a/c - n/a/d - n/b/c - n/b/d - n/c/d - n/a/b/c/d + n/a/b/c + n/a/b/d+ n/a/c/d+n/b/c/d;
	cout << sum << endl;
	return 0;
}

C:Lulu的组合数

奇数项的二项式系数和与偶数项的二项式系数和YCU月赛题解_第1张图片

#include
using namespace std;
typedef long long ll; 
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	ll n, sum;
	cin >> n;
	sum = 1;
	for(int i = 1; i < n; i++){
		sum *= 2;
	}
	cout << sum << endl;
	return 0;
}

一般求a^b的话应该采用快速幂的方法,时间更快,有更优的方法肯定采用优的方法,我建议用这种方法去做

#include
using namespace std;
typedef long long ll; 
ll power(ll a, ll b){
	ll ans = 1;
	while(b){
		if(b & 1) ans *= b;
		b /= 2;
		a *= a; 
	}
	return ans;
} 
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	ll n;
	cin >> n;
	cout << pow(2,n-1) << endl;
	return 0;
}

D:洛洛的圣诞节小铺

此题是一个部分背包问题,题目意思很简单,算出每种物品的性价比,然后从大到小排序,直到钱用完为止。

#include
using namespace std;
struct node{
	int v,w;
	double c;
}a[1005];
bool cmp(node a, node b){
	if(a.c > b.c) return true;
	return false;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, m;
	cin >> n >> m;
	for(int i = 0; i < n; i++){
		cin >> a[i].w;
	} 
	for(int i = 0; i < n; i++){
		cin >> a[i].v;
		a[i].c = 1.0*a[i].v/a[i].w; //算出每种物品的性价比 
	}
	sort(a,a+n,cmp);//从大到小排序 
	double sum = 0;
	int i = 0;
	while(m > 0){
		if(m >= a[i].w) m-=a[i].w,sum+=a[i].v;
		else if(m > 0) sum += a[i].c*m,m=0; 
		i++;
	}
	sum *= 10;
	cout << fixed << setprecision(1) << sum << endl; 
	return 0;
}

E:奇怪的梦

由于题目数据的原因,重新改了一下
这题就是一个一元二次方程解答而已,但要考虑脚只能是偶数,猪和牛不能为负就行,超级简单。

#include
using namespace std;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, m, a, b;
	cin >> m >> n >> a >> b;
	int flag = 0;
	if((m-2*n) % (2*b) == 0) {
		int y = (m-2*n)/(2*b);
		int x = (n-b*y)/a;
		if(x >= 0 && y >= 0){
			cout << x <<" "<< y << endl;
			flag = 1;
		}
	}
	if(flag == 0)	cout << -1 << endl;
	return 0;
}

F:号码游戏

题目的意思就是找出1个或者2个数来。
这就要用到位运算的异或啦,找一个数很简单,相同的异或就为0,剩下的一个值就是要求的答案了。主要讲讲怎样求2个数
首先将所有元素异或得到那两个出现一次的数的异或值,然后寻找这个值的二进制中第一个为1的位置,然后再用它与每个元素按位与,将所有元素进行分组,这时那两个出现一次的数一定在不同组里,而且相同的数一定在同一组里,所以此时再让两个数组都分别进行异或,这样就得到了那两个所求值。

#include
using namespace std;
typedef long long  ll;
const int maxn = 3000000+9;
int a[maxn];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, k, res;
	cin >> n >> k;
	res = 0;
	for(int i = 0; i < n; i++){
		cin >> a[i];
		res ^= a[i];
	}
	if(k == 1){
		cout <<  res << endl;
	}
	else {
		int x, y, flag;
		flag = 1; 
		x = 0, y = 0;
		for(int i = 0; i < 32; i++){   //找到1出现的位置 
			if(res & (flag<<=i)){
				break;
			}
		}
		for(int i = 0; i < n; i++){
			if(flag & a[i]){ //进行分组 
				x ^= a[i];
			}else{
				y ^= a[i];
			}
		}
		if(x > y) swap(x,y); 
		cout << x << " " << y << endl;
	}
	return 0;
}

G:Bobo的位运算

n&-1 = n;//输出n即可

H: Bobo的树(一)

时间戳用处
对于询问 x 是不是 y 的祖先,我们只要判断一下时间戳区间的包含关系
做这题要用到一个叫时间戳的东东,运用第一次访问结点和第二次访问的结点进行判断。知道这个就简单了,用深度优先搜索dfs就可以解决啦

#include
using namespace std;
const int maxn = 1e5+9;
vector<int>e[maxn]; 
int in[maxn],out[maxn];//第一次访问的时间,第二次访问时间 
int cnt;
void dfs(int u, int fa){
	in[u] = ++cnt;
	for(int i = 0; i < e[u].size(); i++){
		if(e[u][i] == fa) { //访问到叶子结点进行下另一个分支 
			continue;
		} 
		dfs(e[u][i],u);
	}
	out[u] = cnt;
	return; 
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, m, k, u, v;
	cin >> n >> m >> k;
	for(int i = 1; i < n; i++){
		cin >> u >> v;
		e[u].push_back(v);//采用向量来存储2个结点的关系 
		e[v].push_back(u);
	}
	cnt = 0;
	dfs(m,-1);
	while(k--){
		cin >> u >> v;
		if(in[u] <= in[v] && out[u] >= out[v]){ 
			cout << 1 << endl;
		}else if(in[u] >= in[v] && out[u] <= out[v]){
			cout << 2 << endl;
		}else{
			cout << 3 << endl;
		}
	}
	return 0;
}

I: Bobo的树(二)

上一题求了树,其实两题差不多,不过后面一题更简单,直接在根节点来一遍搜索记住每一个结点与子结点的权值就行。`

#include
using namespace std;
const int maxn = 1e5+9;
vector<int>e[maxn]; 
int w[maxn];
void dfs(int u, int fa){
	for(int i = 0; i < e[u].size(); i++){
		if(e[u][i] == fa) { //访问到叶子结点进行下另一个分支 
			continue;
		} 
		// w[u] += w[e[u][i]]; //先加会导致错误,自己理解理解 
		dfs(e[u][i],u); //访问子结点 
		w[u] += w[e[u][i]];  //访问完子结点然后再加上子结点的值,由叶子节点往上走。 
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, m, k, u, v;
	cin >> n >> m >> k;
	for(int i = 1; i <= n; i++){
		cin >> w[i];
	}
	for(int i = 1; i < n; i++){
		cin >> u >> v;
		e[u].push_back(v);//采用向量来存储2个结点的关系 
		e[v].push_back(u);
	}
	dfs(m,-1);
	while(k--){
		cin >> u;
		cout << w[u] << endl;
	}
	return 0;
}

J: 整理字符串

这题本人卡了好多遍,题目意思好简单,但有一个坑,特别容易入坑,首先要判断第一个数是否为大小写,但你要考虑全部为空格的,直接换行,友情提醒(不能输空格,只能是换行)

#include
using namespace std;
int main(){
	string str;
	int n;
	cin >> n;
	getchar();
	while(n--){
		getline(cin,str);
		int i = 0;
		for(; i < str.size(); i++){ //找出第一个不是空格的字符 
			if(str[i] != ' ') break; 
		}
		if(i >= str.size()) { //全部是空格,直接换行 
			cout << endl;
			continue;
		}
		int flag = 0; //记录第一个字符是大写还是小写 
		if(str[i] >= 'A' && str[i] <= 'Z'){
			flag = 1;
		}	
		cout << str[i]; //输出第一个字符 
		i++;
		for(; i < str.size(); i++){
			if(str[i] == ' ') continue;
			if(flag == 0) { //当第一个字符为小写 
				if(str[i-1] == ' ' && str[i] >= 'a' && str[i] <= 'z'){
					cout << char(str[i]-32);
					continue;
				}
			} 
			else{//当第一个字符为大写 
				if(str[i] >= 'A' && str[i] <= 'Z' && str[i-1] == ' ') {
					cout << char(str[i]+32);
					continue;
				}	
			}
			cout << str[i];
		}
		cout << endl;
	}
	return 0;
}
       

K: Bobo的挑战

这题有个坑点就是q的值可以是负数。这题求个数的数据不大,用桶排序就ok了,由于有负数,本人考虑用一个二维数组就好。

#include
using namespace std;
const int maxn = 100000+9;
int num[maxn][2];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, m, k;
	cin >> n >> m;
	memset(num,0,sizeof(num));
	for(int i = 0; i < n; i++){
		cin >> k;
		if(k < 0) num[abs(k)][0]++;
		else num[k][1]++;
	}
	while(m--){
		cin >> k;
		if(k < 0)
			cout << num[abs(k)][0] << endl;
		else 
			cout << num[k][1] << endl;
	}
	return 0;
}

L: Wink小学生的分数

其实这题很简单,咱们变个型
x1/x2/x3/x4 = x1x3x4/x2
因为x2无论如何加括号,x2只能当分母,而其他数全部当分子啦,所以这题就转化成求分子分母的公约数啦,当分母为1,这个结果就是整数啦

#include
using namespace std;
const int maxn = 10000+9;
int a[maxn];
int gcd(int a, int b){
	return b == 0 ? a : gcd(b,a%b);
}
bool judge(int m){
	a[1] /= gcd(a[0],a[1]);
	if(a[1] == 1) return true;
	for(int i = 2; i < m; i++){
		a[1] /= gcd(a[i],a[1]);
	}
	return a[1] == 1;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, m, flag;
	cin >> n;
	while(n--){
		flag = 0;
		cin >> m;
		if(m == 1) flag = 1; //如果为一个数,那肯定是整数啦 
		for(int i = 0; i < m; i++){
			cin >> a[i];
		}
		if(flag == 1) {
			cout << "Yes" << endl;
			continue;
		} 
		if(judge(m)){
			cout << "Yes" << endl;
		}
		else{
			cout << "No" << endl;
		}
	}
	return 0;
}

M: Lulu的线段树

这题不要被线段树吓到了(也可能有些人不知道啥是线段树,不知道还好,刚开始看这题我被吓到了)这题就是按步骤一步步来就行,需要注意一下靠前的那个条件就行啦,简单,直接上代码

#include
using namespace std;
int a[1001];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, m;
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
	} 
	while(m--){
		int l, r,x,y;
		cin >> x >> y;
		int maxx = -1;
		int minn =  1009;
		for(int i = x; i <= y; i++){
			if(maxx < a[i]){
				maxx = a[i];
				l = i;
			}
			if(minn > a[i]){
				minn = a[i];
				r = i;
			}
		}
		swap(a[l],a[r]);
	}
	for(int i = 1; i < n; i++){
		cout << a[i] << " ";
	} 
	cout << a[n] << endl;
	return 0;
}

你可能感兴趣的:(题解)