“蔚来杯“2022牛客暑期多校训练营(加赛)蒟蒻补简单题H + E + J

思路

表面上看是lca的题,但实际上是一个简单的树的基础问题

对于一个结点x,树上所有点与其的lca只有两种情况,第一种是它本身和它的子树内的点与其lca是它自己,其他所有点的lca都在其祖先结点上,我们只需要递归处理x的祖先结点的值即可。算在每个结点的答案是(sz【fa】 - sz【x】) * fa 也就是祖先结点子树

题目要求输出后缀0,这样我们要维护的答案就变了,我们需要统计答案中2 和 5 的次数,后缀0的个数是两个数的较小值

代码实现

#include<bits/stdc++.h>
#define x first
#define y second
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5 + 10, mod = 1e9 + 7, INF = 1e9;
int n, m;
int h[N], e[N], ne[N], idx;
int fa[N], sz[N];
void add (int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
int dfs (int u, int father)
{
	int s = 1;
	
	for (int i = h[u]; ~i ; i = ne[i])
	{
		int j = e[i];
		if (j == father) continue;
		
		fa[j] = u;
		s += dfs (j, u);
	}
	
	sz[u] = s;
	return s;
}
int query (int x)
{
	int t = x;
	int cnt1 = 0, cnt2 = 0;
	while(t % 2 == 0)
		cnt1 ++ ,t /= 2;
	t = x;
	while(t % 5 == 0)
		cnt2 ++ ,t /= 5;
	int res1 = sz[x] * (cnt1), res2 = sz[x] * (cnt2);
	int f = fa[x];
	
	while (f != 0)
	{
		t = f;
		cnt1 = 0, cnt2 = 0;
		while(t % 2 == 0)
			cnt1 ++ ,t /= 2;
		t = f;
		while(t % 5 == 0)
			cnt2 ++ ,t /= 5;
		res1 += (sz[f] - sz[x]) * cnt1 , res2 += (sz[f] - sz[x]) * cnt2;
		x = f;
		f = fa[f];
	}
	
	return min(res1 , res2);
}
signed main()
{
	ios::sync_with_stdio (0);
	cin.tie (0);
	cin >> n >> m;
	memset (h, -1, sizeof h);
	
	for (int i = 0; i < n - 1; i ++)
	{
		int a, b;
		cin >> a >> b;
		add (a, b);
		add (b, a);
	}
	
	dfs (1, -1);

	while (m --)
	{
		int x;
		cin >> x;
		cout << query (x) << endl;
	}
}

思路

对于每个人来说,他得到的冰红茶只有三种情况
Ai , 0 ,-Ai 如果一个人在p - 1 人之后复读,他就有可能会得到负收益,这是每个人都不能接受的,也就是每个人都会尽力避免负收益,最好情况就是只有不到p个人参加复读,而在不到p人复读的情况下,每个人肯定是能复读就复读,考虑到p可能小于n,所有总结就是只有n%p个人得到冰红茶

代码实现

	int n , p;
	cin >> n >> p;
	for(int i = 0 ; i < n ; i ++)
		for(int j = 0 ; j < n ; j ++)
			cin >> a[i][j];
	
	for(int i = 0 ; i < n ; i ++)
	{
		if(i < n % p) cout << a[i][i] << ' ';
		else cout << 0 << ' ';
	}

思路

考虑让所有元素相等就想到差分,只要差分数组全为0即达到目的,本题差分跟原本差分不同,b【i】 = a【i】- a【i-1】 + 3 % 3

不难发现差分数组中有1的地方才能实现操作,对于每两个相邻位置(x , y)有y = 1 时才会对x有影响
(0 , 1) 可以变成( 1 , 0 ) (1 , 1 ) 可以变成(2 , 0) (2 , 1)可以变成(0 , 0)

也就是说1 的位置可以在有0的地方随意移动, 1可以变成2,2 和 1可以抵消
所以只需1的个数大于等于2的个数即可
注意这是一个循环的差分数组,即b【1】 = a【1】 - a【n】

代码实现

#include<bits/stdc++.h>
#define x first
#define y second
#define int long long
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, mod = 1e9 + 7, INF = 1e9;
int a[N];
int b[N];
void solve()
{
	int n;
	cin >> n;
	
	for (int i = 1 ; i <= n ; i ++)
		cin >> a[i];
		
	a[0] = a[n];
	
	for (int i = 1 ; i <= n ; i ++)
		b[i] = ( (a[i] - a[i - 1]) + 3) % 3;
		
	int cnt1 = 0, cnt2 = 0;
	
	for (int i = 1 ; i <= n ; i ++)
	{
		if (b[i] == 1) cnt1 ++;
		else if (b[i] == 2) cnt2 ++;
	}
	
	if (cnt1 >= cnt2) cout << "Yes" << endl;
	else cout << "No" << endl;
}
signed main()
{
	std::ios::sync_with_stdio (false);
	std::cin.tie (nullptr);
	int t = 1;
	cin >> t;
	
	while (t--)
		solve();
		
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值