2020牛客寒假算法基础集训营4

题目链接:https://ac.nowcoder.com/acm/contest/3005
题解链接:https://ac.nowcoder.com/discuss/365889?type=101&order=0&pos=5&page=1

A - 欧几里得

反欧几里德,根据1、0来求相应的最小原始数,可以直接进行欧几里德算法的逆运算
也可以打表找规律,发现答案是斐波那契数列:0、1、1、2、3、5、8…

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;

int main(void)
{
	int t;
	ll n, x, y;
	cin >> t;
	while (t--){
		cin >> n;
		x = 1; y = 0;
		while (n--){
			swap(x, y);
			while (x <= y){
				x += y;
			}
		}
		cout << x + y << endl;
	}
	
	return 0;
}

B - 括号序列

遍历字符串,如果是左半部分就进栈
如果是右半部分且栈不为空就进行匹配,能匹配上就使当前栈顶元素出栈,匹配不上就使其进栈。
最后如果栈为空说明合法,栈不为空说明不合法

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e6 + 5;

char st[N];

int main(void)
{
    int t = 0;
    string s;
    cin >> s;
    int len = s.size();
    for (int i = 0; i < len; i++){
        if( t > 0 && s[i] == ')' && st[t] == '(') t--;
        else if (t > 0 && s[i] == '}' && st[t] == '{') t--;
        else if (t > 0 && s[i] == ']' && st[t] == '[') t--;
        else st[++t] = s[i];
    }
    if (t > 0) cout << "No" << endl;
    else cout << "Yes" << endl;
    
    return 0;
}

C - 子段乘积

本题可以使用线段树,也可以分治,还可以使用尺取法
尺取法做法:维护当前区间中有几个0,同时维护不是0的数字的乘积。因为是除法的取模,所以这种方法需要使用乘法逆元。
时间复杂度为 O ( n ) O(n) O(n)

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5, M = 998244353;
typedef long long ll;

int arr[N];

ll mod_pow(ll x, ll n, ll mod)
{
	ll res = 1; 
	while (n > 0){
		if (n & 1) res = res * x % mod;
		x = x * x % mod;
		n >>= 1;
	}
	return res % mod;
}

int main(void)
{
	int n, k, s;
	int l, r, num;
	ll sum, ans, cnt;
	scanf("%d%d", &n, &k);

	for (int i = 0; i < n; i++)
		scanf("%d", &arr[i]);
	//初始化
	l = 0; r = 0;
	sum = 1;
	ans = -1;
	cnt = 0;
	num = 0;
	while (true)
	{
		//右端点右移 
		while (r < n && num < k)
		{
			if (arr[r])
				sum = (sum * arr[r]) % M;
			else
				cnt++;
			r++;
			num++;
		}
		//遍历结束
		if (r >= n)
			break;
		//更新答案
		if (cnt == 0)
			ans = max(sum, ans);
		else
			ans = max((ll)0, ans);
		//左端点右移 
		if (arr[l] != 0)
			sum = (sum % M) * mod_pow(arr[l], M - 2, M) % M;
		else
			cnt--;
		l++;
		num--;
	}
	if (ans == -1)//不存在区间和大于s的区间
		printf("0\n");
	else
		printf("%d\n", ans);	
	
	return 0;
}

线段树解法
直接枚举区间的乘积就可以,复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5, M = 998244353;
typedef long long ll;

struct node{
	int l, r;
	ll val, lazy;
}t[N << 2];

int n, m, l, r;
char c[2];
ll ans, maxv;

void push_up(int x)//更新和值(建树、修改)
{
	t[x].val = (t[x * 2].val * t[x * 2 + 1].val) % M;
}

void build(int x, int l, int r)//建树
{
	t[x].l = l; t[x].r = r;
	if (l == r){
		scanf("%lld", &t[x].val);
		return;
	}
	int mid = (l + r) >> 1;
	build(x * 2, l, mid);
	build(x * 2 + 1, mid + 1, r);
	push_up(x);
} 

ll query(int x, int l, int r)//区间查询
{
	if (t[x].l == l && t[x].r == r)
		return t[x].val;
	//push_down(x);
	int mid = (t[x].l + t[x].r) >> 1;
	if (r <= mid)
		return query(x * 2, l, r) % M;
	else if (l > mid)
		return query(x * 2 + 1, l, r) % M;
	else
		return (query(x * 2, l, mid) % M) * (query(x * 2 + 1, mid + 1, r) % M) % M;
} 

int main(void)
{
	int n, k;
	scanf("%d%d", &n, &k);
	build(1, 1, n);
	
	for (int i = 1; i <= n + 1 - k; i++){
		maxv = query(1, i, i + k - 1) % M;
		ans = max(ans, maxv);
	}
	printf("%lld\n", ans);
	
	return 0;
}

D - 子段异或

如果 [ l , r ] [l, r] [l,r]异或和为0,则说明 x o r s u m [ l − 1 ] ⨁ x o r s u m [ r ] = 0 xorsum[l-1]\bigoplus xorsum[r]=0 xorsum[l1]xorsum[r]=0,即 x o r s u m [ l − 1 ] = x o r s u m [ r ] xorsum[l-1]=xorsum[r] xorsum[l1]=xorsum[r]

所以只需要求出所有的前缀和,用 m a p map map记录每个前缀和有多少个等于它即可,还需要注意前0个数的前缀和等于0,需要额外记录一次。

#include <cstdio>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;

map<ll, ll> m;
map<ll, ll>::iterator it;

int main(void)
{
	ll n, t, x = 0;
	ll ans = 0;
	scanf("%lld", &n);
	m[0] = 1;
	for (int i = 1; i <= n; i++){
		scanf("%lld", &t);
		x ^= t;
		m[x]++;
	}
	for (it = m.begin(); it != m.end(); it++){
		n = it -> second;
		ans += n * (n - 1) / 2;
	}
	printf("%lld\n", ans);
	
	return 0;
}

F - 树上博弈

当两人间的距离为偶数时,总是牛牛胜。否则总是牛妹胜。
因为每次两人走完之后,距离的奇偶不变,所以只需要统计深度为奇数和偶数的节点个数
用异或1将其深度转化为0或1来表示奇数和偶数,再统计0、1的个数即可
两人同为奇数深度和同为偶数深度的情况即为答案

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;

int depth[N];
ll cnt[2];

int main(void)
{
	int n, t;
	scanf("%d", &n);
	depth[1] = 0;
    cnt[0] = 1;
	for (int i = 2; i <= n; i++){
		scanf("%d", &t);
		depth[i] = depth[t] ^ 1;
		cnt[depth[i]]++;
	}
	ll ans = cnt[0] * (cnt[0] - 1) + cnt[1] * (cnt[1] - 1);
	printf("%lld\n", ans);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值