Codeforces Global Round 21 C ~ D题解

C

题意

给定两个数组a,b,以及一个正整数m, 可以对a进行若干次两种操作

  1. 选择一个长度为m且全部数字相同的子数组,假设数字为x,则将它们合并为一个数m*x。
  2. 选择a中一个m的倍数x,将其分割成m个m/x。顺序保持和原来一致。
    问能否将a变成b数组

思路

很简单的想到操作1 、 2两种操作是可逆的,但是 4 8 4 变成 16这样的操作我们难以模拟,不妨就让a, b数组一起进行操作2 看最后的数组一不一样即可

代码实现

唯一一个难点就是数组进行操作二后可能非常大,我们考虑用pair<int,longlong> 【x, v】来存储连续的 x 的个数 v
vector<PII> divide (vector<int> p, int m)
{
	vector<PII> res;
	int len = p.size();
	
	for (int i = 1 ; i < len; i ++)
	{
		if (p[i] % m == 0)
		{
			int k = p[i];
			int cnt = 1;
			
			while (k % m == 0)
			{
				k /= m;
				cnt *= m;
			}
			
			res.push_back ({k, cnt});
		}
		else res.push_back ({p[i], 1});
	}
	
	return res;
}
vector<PII> merge (vector<PII> p)
{
	vector<PII> res;
	int last = -1;
	
	for (int i = 0; i < p.size(); i ++)
	{
		if (p[i].x == last) res[res.size() - 1].y += p[i].y;
		else res.push_back ({p[i].x, p[i].y});
		last = p[i].x;
	}
	
	return res;
}
void solve()
{
	int n1, n2, m;
	cin >> n1 >> m;
	vector<int> a (n1 + 1);
	
	for (int i = 1 ; i <= n1 ; i ++)
		cin >> a[i];
		
	cin >> n2 ;
	vector<int> b (n2 + 1);
	
	for (int i = 1 ; i <= n2 ; i ++)
		cin >> b[i];
		
	auto A = divide (a, m);// divide 表示将数组进行操作2
	auto B = divide (b, m);
	A = merge (A); B = merge (B);// merge 表示将数组进行合并
 
	if (A.size() == B.size())
	{
		bool ok = true;
		int len = A.size();
		
		for (int i = 0; i < len ; i ++)
			if (A[i].x != B[i].x or A[i].y != B[i].y)
				ok = false;
				
		if (ok) cout << "Yes" << endl;
		else cout << "No" << endl;
		
		return ;
	}
	
	cout << "No" << endl;
}

D

题意

给定一个排列,选取一个区间 l, r 如果 [ l , r ] 区间中左右两个端点是这个区间的最大和最小值,那么给l , r连一条边权为1 的边 ,问建边完后从1走到n的最短距离是多少?

思路

建图是陷阱,n方的建图完全不行,只需要看怎么从1走到n即可,我们可以发现整个区间的最大和最小值一定会成为路径上的某个点,
这个题就变成了选最少的点从1跳到n,其中最大最小值一定要选。
以最小值为例:最小值左右分成两个区间分别需要这两个分区间的最大值来作为跳板,在分治下去,直到1和n连通。

需要完成这样的操作,我们需要预处理前缀最小值,最大值,后缀最小值,最大值

代码实现

void solve()
{
	int n;
	cin >> n;
	vector<int> a (n);
	
	for (int i = 0; i < n ; i ++)
		cin >> a[i];
		
	int mi = min_element (a.begin(), a.end()) - a.begin();
	vector<int> lmin (n), lmax (n), rmin (n), rmax (n);
	lmin[0] = rmin[0] = 0;
	
	for (int i = 1 ; i < n ; i ++)
	{
		if (a[i] < a[lmin[i - 1]]) lmin[i] = i;
		else lmin[i] = lmin[i - 1];
		
		if (a[i] > a[lmax[i - 1]]) lmax[i] = i;
		else lmax[i] = lmax[i - 1];
	}
	
	rmax[n - 1] = rmin[n - 1] = n - 1;
	
	for (int i = n - 2 ; i >= 0; i --)
	{
		if (a[i] < a[rmin[i + 1]]) rmin[i] = i;
		else rmin[i] = rmin[i + 1];
		
		if (a[i] > a[rmax[i + 1]]) rmax[i] = i;
		else rmax[i] = rmax[i + 1];
	}
	
	int id = mi;
	int res = 0;
	
	while (id)
	{
		id = lmax[id];
		res ++;
		
		if (!id) break;
		
		id = lmin[id];
		res ++;
	}
	
	id = mi;
	
	while (id < n - 1)
	{
		id = rmax[id];
		res ++;
		
		if (id >= n - 1) break;
		
		id = rmin[id];
		res ++;
	}
	
	cout << res << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值