Codeforces Round #814 (Div. 2) C + D

C

题意

给定n个数,每次队首两个数比拼数大小,大的留在队头,小的去队尾,给定x , k 问初始序列时第x位在前k轮赢了多少次?

思路

维护每个数的第一个比它大的数的索引和每个数是否是前缀最大值

可以知道如果一个数前面有比他大的数,那么他的胜场只能为0,所以处理询问时如果a【x】不是前缀最大值就直接输出0
否则根据k的大小再来输出答案:设p为维护的比x第一个大的数的索引,可以知道第k轮涉及到前k+1个数的比对,所以p <= k + 1 时即当前比试的轮次x已经输过了,直接输出p和x的位置差即可,p > k + 1时,说明x还没有输或者还没有轮到x比大小,根据情况输出0 或者 k + 1个数和x的差值

需要注意的是

  1. x == 1这个位置非常特殊需要特判
  2. 维护第一个比x大的数的索引pos【x】暴力枚举是会超时的,我们需要用之前维护的值来加快这个维护,给定位置i
    如果a【i-1】 < a【i】那么pos【i-1】直接就是i,否则我们可以跳到pos【i】因为如果a【i-1】> a【i】那么a【i-1】一定大于(i, pos【i】- 1)中的所有值。

代码实现

int a[N];
int pos[N];
bool win[N];
void solve()
{
	int n, q;
	cin >> n >> q;
	memset (a, 0, sizeof a);
	memset (pos, 0, sizeof pos);
	memset (win, false, sizeof win);
	
	for (int i = 1 ; i <= n ; i ++) cin >> a[i];
	
	int ma = a[1];
	win[1] = a[1] > a[2];
	
	for (int i = 2 ; i <= n ; i ++)
	{
		if (ma > a[i]) win[i] = false;
		else
		{
			ma = a[i];
			win[i] = true;
		}
	}
	
	pos[n] = INF;
	
	for (int i = n  ; i >= 2 ; i --)
	{
		if (a[i - 1] < a[i]) pos[i - 1] = i;
		else
		{
			int p = pos[i];
			
			while (p != INF and a[i - 1] > a[p])
				p = pos[p];
			
			pos[i - 1] = p;
		}
	}

	while (q --)
	{
		int x, k;
		cin >> x >> k;
		
		if (!win[x]) cout << 0 << endl;
		else
		{
			int p = pos[x];
			
			if (p <= k + 1)
			{
				if (x == 1) cout << p - x - 1 << endl;
				else cout << p - x << endl;
			}
			else
			{
				int y = max (1ll, x - 1);
				cout << max(0ll , k - y + 1) << endl;
			}
		}
	}
}

D

题意

给定一个序列,每次可以选择一个区间[ l , r ],使区间中所有数异或上一个任意值,花费是区间长度 / 2向上取整,询问最少需要多少代价能使序列中所有数变成0

思路

可以发现每次只取长度为2的区间和取长度大于2的区间的总操作区间 和花费都是一样的。且每次只操作一个数必定能花费n使所有数变为0.
所以我们每次只操作长度为1或2的区间,从1到n操作,寻找是否能减少操作次数,即用一次操作将两个数都变成0

我们发现我们在之前的操作可以让Ai 这个数变成【1 - i】任意组合的异或后缀和,也就是说,我们当处理到i位置的时候,根据需求,这个Ai可能为1 - i任意异或后缀和,如果此时有Ai+1 等于某个异或后缀和,那么我们可以用1的花费将i, i+1两个位置变成0

代码实现

p 表示 当前异或后缀和,set里存的是之前所有异或后缀(当成功一次消除两个时需要清空set和p,因为之前的异或后缀已经用不上了)

	int n;
	cin >> n;
	int res = 0;
	set<int> s;
	int p = 0;
	s.insert (0);
	
	for (int i = 1 ; i <= n ; i ++)
	{
		int x;
		cin >> x;
		
		if (x == 0 or s.count (x ^ p))
		{
			s.clear();
			s.insert (0);
			p = 0;
		}
		else
		{
			s.insert (p);
			p ^= x;
			res ++;
		}
	}
	
	cout << res << endl;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值