Codeforces Round #807 (Div. 2)题解:CDE

C: 首先我们要看到比较明显的一点那就是c的值最大40,这意味着我们可以暴力回退:

是一道比较注意细节的模拟题

#include<iostream>
#include<string>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int maxn = 2e5 + 10;
int a[maxn], n, m,q;
int sum[maxn];
char s[maxn];
pair<int, int>p[maxn];
void solve()
{
	cin >> n >> m >> q;
	sum[1] = n;
	cin >> s+1;
	p[0].first = 1, p[0].second = n;
	for (int i = 1; i <= m; i++){
		cin >> p[i].first >> p[i].second;
		sum[i + 1] = sum[i] + p[i].second - p[i].first+1;
	}
	while (q--)
	{
		int t;
		cin >> t;
		int now = m+1;
		while (now!=1)//模拟回退过程,一直回退到初始的区间
		{
			if (t <=sum[now - 1])
			{
				now--;
			}
			else
			{
				int now1 = t - sum[now - 1];
				t = p[now - 1].first + now1-1;
				now--;
			}
		}
		cout << s[t] << endl;
	}
}
signed main(){
	int t=1;
	cin >> t;
	while (t--){
		solve();
	}
}

 D: 我们将其分为不同的01串,可以确定的一点是,假如这个串数量大于1,那这个区间就可以任意将左右两边进行吞并和吐出:001100->011110,这个条件是确保左右不嫩将左右两边区间进行吞并,所以再保证0101区间的情况下,所以我们再保证01区间一样的前提下再查找最小操作次数:

#include<iostream>
#include<string>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int maxn = 2e5 + 10;
int a[maxn], n, m,q;
int sum[maxn];
char s[maxn];
string s1, s2;
int cnt[maxn], cnt2[maxn];
void solve()
{
	cin >> n;
	cin >> s1 >> s2;
	if (s1[0] != s2[0] || s1[n - 1] != s2[n - 1])//确保是一眼的01交替区间
	{
		cout << -1 << endl;
		return;
  }

	int len1 = 1, len2 = 1;
	cnt[0] = 0, cnt2[0] = 0;
	cnt[1] = 1, cnt2[1] = 1;
	for (int i = 1; i < n; i++){
		if (s1[i] == s1[i - 1])
		{
			cnt[len1]++;
		}
		else
		{
			cnt[++len1] = 1;
		}
	}
	for (int i = 1; i < n; i++) {
		if (s2[i] == s2[i - 1])
		{
			cnt2[len2]++;
		}
		else
		{
			cnt2[++len2] = 1;
		}
	}
	if (len1 != len2)
	{
		cout << -1 << endl;
	}
	else
	{
		int ans = 0;
		for (int i = 1; i <len1; i++){//模拟过程
			if (cnt[i] < cnt2[i]){
				ans += abs(cnt[i] - cnt2[i]);
				cnt[i + 1] -= abs(cnt[i] - cnt2[i]);
			}
			else if(cnt[i]>cnt2[i])
			{
				cnt[i + 1] += abs(cnt[i] - cnt2[i]);
				ans += abs(cnt[i] - cnt2[i]);
			}
		}
		cout << ans << endl;
	}
}
signed main(){
	int t=1;
	cin >> t;
	while (t--){
		solve();
	}
}

E:

我们先将其正确维护后 找最左边的1的位置就是答案,一般是套二分来查找,我找到了一篇直接跑线段树的题解,虽然速度比较慢

Codeforces Round #807 (Div. 2) A - E - 哔哩哔哩 (bilibili.com)

然后就是修改,假如cnt[a[x]]=1,我们就将其删掉就好了,假如不是,我们要从这个点出发,找到其右边最近的1,并将这些点均翻转:

#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 3e5 + 10;
int n, m, a[maxn], cnt[maxn];
struct node
{
	int l, r;
	int sum;
	int rev;
}tree[maxn<<2];
void pushup(int rt)
{
	tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}
void pushdown(int rt)
{
	if (tree[rt].rev)
	{
		tree[rt << 1].sum = tree[rt << 1].r - tree[rt << 1].l + 1 - tree[rt << 1].sum;
		tree[rt << 1|1].sum = tree[rt << 1|1].r - tree[rt << 1|1].l + 1 - tree[rt << 1|1].sum;
		tree[rt << 1].rev ^= 1;
		tree[rt << 1 | 1].rev ^= 1;
		tree[rt].rev = 0;
	}
}
void build(int rt, int l, int r)
{
	tree[rt].l = l, tree[rt].r = r;
	if (l == r)
	{
		tree[rt].sum = cnt[l];
		return;
	}
	int mid = l + r >> 1;
	build(rt << 1, l, mid);
	build(rt << 1 | 1, mid + 1, r);
	pushup(rt);
}
void modify(int rt, int l, int r)
{
	if (l <= tree[rt].l&&r >= tree[rt].r)
	{
		tree[rt].sum = tree[rt].r - tree[rt].l + 1 - tree[rt].sum;
		tree[rt].rev ^= 1;
			return;
	}
	pushdown(rt);
	int mid = tree[rt].l + tree[rt].r >> 1;
	if (l <= mid)
		modify(rt << 1, l, r);
	if (r > mid)
		modify(rt << 1 | 1, l, r);
	pushup(rt);
}
int query(int rt, int ql,int qr)
{
	if (ql <= tree[rt].l&&qr >= tree[rt].r)
	{
		return tree[rt].sum;
	}
	pushdown(rt);
	int res = 0;
	int mid = tree[rt].l + tree[rt].r >> 1;
	if (ql <= mid)
		res+=query(rt << 1, ql, qr);
	if (qr > mid)
		res+=query(rt << 1 | 1, ql, qr);
	pushup(rt);
	return res;
}
int ask1(int rt, int x)
{
	if (tree[rt].r < x || tree[rt].sum == 0)return -1;
	if (tree[rt].l == tree[rt].r)return tree[rt].l;
	else
	{
		pushdown(rt);
		int t = ask1(rt << 1, x);
		if (~t)
			return t;
		return ask1(rt << 1 | 1, x);
	}
}
int ask0(int rt, int x)
{
	if (tree[rt].r < x || tree[rt].sum ==tree[rt].r-tree[rt].l+1)return -1;
	if (tree[rt].l == tree[rt].r)return tree[rt].l;
	else
	{
		pushdown(rt);
		int t = ask0(rt << 1, x);
		if (~t) {
			return t;	
		}	return ask0(rt << 1 | 1, x);
	}
}
int ans(int rt)//找最左边1
{
	if (tree[rt].l == tree[rt].r)return tree[rt].l;
	else
	{
		pushdown(rt);
		int mid = tree[rt].l + tree[rt].r>>1;
		if (tree[rt << 1 | 1].sum)return ans(rt << 1 | 1);
		else
			return ans(rt << 1);
	}
}
signed main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i]; cnt[a[i]]++;
	}
	for (int i = 0; i < maxn - 1; i++)
	{
		cnt[i + 1] += cnt[i] / 2;
		cnt[i] %= 2;
	}
	build(1, 1, maxn - 1);
	while (m--)
	{
		int x, y;
		cin >> x >> y;
		int t = query(1, a[x],a[x]);
		if (t == 1)
		{
			modify(1, a[x], a[x]);
		}
		else
		{
			int l = a[x], r = ask1(1, a[x]);//找a[x]右边最靠近的1
			modify(1, l, r);
		}
		a[x] = y;
		t = query(1, a[x], a[x]);
		if (t == 0)
		{
			modify(1, a[x], a[x]);
		}
		else
		{
			int l = a[x], r = ask0(1, a[x]);
			modify(1, l, r);
		}
		cout << ans(1) << endl;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值